Let’s talk about React and MVP

Trying new approach to our React application’s architecture

Some months ago at PDVend we decided to rebuild our web frontend, because it was hard a lot to ship new features or even solve bugs. With this decision, it also came the worry to not make the same architectural mistakes again.

After a study phase we tried a proof of concept with Angular 1 and Typescript that failed miserably. We were not sure if it was because of the stack choice or because of us. After it, we choose to adopt React, because the whole team as comfortable with, and Redux, much by the community hype around it.

Three months later, here we are: the team feels to be writing so much boilerplate and somehow premature scaling, even if this “scale” is “preparing things to scale” and not effective evolving something. Debugging is our worst nightmare, followed of “where do I put this code?” feels.

We decided to review the choice of Redux, since we agreed that React is still good to go. Then I suggested to the Frontend team to take a look on Android team, since we already have an app running in production. After it, we got the insight that MVP (the architectural choice of Android’s team) would be nice with React. Here starts our saga to use structure a React application with MVP and get all of the benefits that we’ve noted on Android app.


What is MVP?

Model-View-Presenter is architectural pattern derived from Model-View-Controller, the main difference is that, while in MVC the model fires updates in the view, in MVP it only relies on the presenter. Martin Fowler brought the concepts of Supervising Controller and Passive View.

How we use MVP?

To make it easier to our team, we made some statements that allowed to structure the application:

The view is (almost) dummy

In general, our views must not handle any type of business logic. However, they are allowed to handle presentational logic.

For example: think about a login form; If you use controlled inputs, for every character the user types on email/password fields, you’ll receive an onChange event. This values won’t have any use to the application until the user clicks the login button.

Our approach is to handle this “temporary” value in the View layer and call presenter on meaningful interactions. Otherwise, our presenter would be so much coupled with the view events.

Presenter controls what is happening in the view, but not how

It’s part of the responsibilities of the Presenter Layer to say what should the view do, like show loading, hide loading, show this error message, navigate to the dashboard page and so on.

Presenter should not, however, say to the view how it should do the job. It’s part of the view responsibilities to choose if it will show the error message into a modal or inline error field, or if it loading will hide div#login-form and show div#loading-widget.

Presenter should be agnostic to the view technological choice. It’s nice to think the case you change the view from, for example, snackbar to sweetalert, if you would keep the presenter intact.

Everything is an Interface

Javascript hasn’t types, we know. Nothing prevents us from defining interfaces (or contracts) between the layers of our application. The main advantage of doing this is that it make everything so much easy to test, mocks/stubs are easier to write.

Even if the “contracts” could be hardly checked with some duck typing, we consider that doing this is too much. We preferred just to respect while coding, each layer believes that the methods it will call exists in the called object.

Note: Specifically the React implementation of the View layer can check using propTypes, we can define what we expect of the Presenter by using React.PropTypes.shape.

Presenter don’t handle models directly

This is a bit more specific to our application, not all apps will need to do this.

Since one of the requirements of our application is to work offline, we have data distributed into our webservice and into user’s local database. We agreed that it would be too much logic to the presenter. Then surged the datasource.

Datasource is the entity responsible to decide from where fetch/write data. For example, if the user hasn’t internet, it can save into local database or return an error, depending of the entity being accessed. In fetch operations, it can decide if will return from local storage to be faster or from service to be more consistent.

As I said, it’s a bit specific to applications that have more that one source for it’s data. Don’t be ashamed of calling the service or local storage classes from your presenter if it isn’t your case.

Not every view needs a Presenter

It’s common to find some view that does not handle any type of business logic. For example the component that handles the application layout, defining where sidebar will be and what is the content space.

This components don’t need a dummy presenter just to follow the architecture defined. It makes the code more clean and easier to read.


What we’ve done so far

As a proof of concept, we made this sample application. It illustrates all what I’ve explained above in a login screen. (It’s nice to navigate through the commits to understand the evolution of the whole thing)

Functionally

Using React, MVP and progressive web app structure, we’ve accomplished the following:

  • When the user access it for the first time, service workers will cache the application and let the user access it even if there isn’t internet or the server is down.
  • When the user log in for the first time, it will use API to authenticate. If there’s no access to the server, user will receive an error message indicating this. But, if there’s internet and the login is done successfully, user data will be stored into local database. (We’re not going to enter into the security issue here)
  • After the first access and login, user can use it fully offline, since the application is cached and it’s data from the first login can be used to re-authenticate.
  • Not implemented but it’s a possibility: When the user access the login screen, we could show an list of local users, improving the speed of login, since user will not need to type it’s email, only password.

Stack choice

What it looks like


Conclusion

The first benefit that we’ve noted about this architecture is that it does not take too long to code the initial structure. Of course, took some time to imagine how to fit things better with React, but that wasn’t a big deal.

The second point is it’s “plugableness”, we can easily switch from Axios to fetch, change the local storage library or even replace React with other view library, allowing us to reuse all the business logic into, for example, an desktop app written in javascript (e.g. Electron).


Thanks for reading! Feel free to leave your comment.

Like what you read? Give Gabriel Teles a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.