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 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
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
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
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)
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.
- Base code: Ejected Create React APP + Create Progressive PWA
- View: React, react-flexbox-grid, Semantic UI, UI-Router
- Presenter: Vanilla JS
- Model: Vanilla JS (datasource), Axios (Service classes), PouchDB (local storage).
What it looks like
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.
Thanks for reading! Feel free to leave your comment.