Motivation to talk about Pure Model-View-Presenter in Android:
It is quite complicated to find in the software development industry a standard in terms of architecture in Android development. It is true that for some time a very basic MVP has been proposed to break the God-Object (Activities responsible for absolutely everything) and, since not so long ago, a MVVM proposed by Google, using its architecture components, is being adopted (ViewModel, LiveData , LifeCycleObserver …). Although these architectures (especially MVVM) fulfill the purpose of decoupling components and making them testable and reusable, we still find several points that make us ask ourselves if this is the best architecture we can use:
1.In the first case, the view and the presenter are known, as well as the presenter and the model. Regarding the MVVM, although the ViewModel does not know the view, the view does know the ViewModel, which makes reusing views with different ViewModels complicated. The principle of investment of dependencies (DIP) is respected only in one direction (view => service, and not service => view). This problem exists since an investment of dependencies in both directions gives us greater cohesion and less coupling, but also increases complexity. It is up to the reader to determine if this trade-off is worth it.
DIP both directions
- As for the communication between the layers, in the case of the classic MVP we find that it is done through callbacks (which will eventually turn our application into a callback hell), while in the MVVM LiveData is used and although it allows us to avoid callbacks, it does not provide us with a large number of operators to manipulate the data (at the moment of writing this, we only have map and switchmap).
These are the primary reasons that lead us to try to find a better solution when writing our applications.
Pure Model-View-Presenter or Controlerless architecture
In our case, we opted for an architecture called Pure Model-View-Presenter (which we also usually label as controllerless architecture) that allows us to completely uncouple the view of the model layer. This is possible thanks to the presenter, whose sole function is to connect a single view with a single application service (also called interactor, or use case); so that, to build a screen, we can use N presenters, one for each pair of view-services.
To illustrate what I have explained in this article I have published a small example/project on github ( Pure Model-View-Presenter ): https://github.com/apiumhub/mvpp-android
Thus, we can understand our view and our service as black boxes that emit events and receive information ( through input and output “cables” ), and our presenter will be responsible for connecting the input cables of a component to those of output from another and vice versa.
In addition, the fact of using lambdas to communicate the view with the service, allows us to avoid exposing the Observables among these components directly. Like this we could use RxJava in our service, so we could manipulate the data with all the operators that this library provides us; and LiveData in the part of the view, which will allow us to make an implementation that is aware of the changes in the life cycle of our activity, or even use the ViewModel provided by Google for that purpose.
In the case of Android, the implementation of our view will correspond to an Activity or a Fragment (Fragment in the example/project we looked at), but it could be a ViewModel or even a visual component. The case that the view is an Activity formed by N fragments is not contemplated here, since each fragment will have M presenters and we could assume that the view would be each of those fragments, although they are then grouped in one (or even within another fragment).
Our service will be responsible for applying all the business logic and orchestrate the different domain services or directly, the repositories that will be responsible for providing / storing information to / from the application.
In the layer that is responsible for providing data to the application, or storing the one that is necessary (Gateway, BD, SharedPreferences, Cache …). Each of these communication channels will be implemented with a repository pattern, which will be injected into the service where they are needed.
What do we win?
It is thanks to having the dependency service => view inverted, and not only the dependency view => service, that we can do things such as:
- Add / remove more listeners in view in runtime
- Use the same view with more than one “listener” at the same time. For example when, in the click of a button, we have to communicate with the server and launch a tracking event.
- By debugging our application, we can see in a single site, the presenter (s), all event flows.
- To be able to apply the “presenter first” design, before the View and Service components, define the interfaces of the two, such as methods and events
At the time of writing tests for our application it is important that we can test isolated units of code, that do not have side effects in other parts of the application, and whose dependencies can be mocked ( that these code units do not depend on other parts of the application). Thanks to the fact that in this architecture, our components are black boxes that receive events and emit information, it is easy to verify that, when receiving an X event, an information Y is issued.
The infrastructure layer (data) is also easily testable since all we have to do is mock the server’s responses, and for that we will use MockWebServer from OkHttp.
Where are we going?
Currently there are architectures such as Redux and Redux-saga that work under the principle of investment of dependencies, in the sense that they are totally Event-Driven. Observing the evolution of frontend architectures it is not unreasonable to think that in Android we are approaching an architecture similar to Redux.
There are many things that can be improved, for example:
- ViewModel : An additional layer can be added between the View and the Presenter that is responsible for saving the state of the view. In addition, this ViewModel could be injected with DataBinding directly into the XML and, using LiveData, make the binding of visual components to LiveData’s Observables. Also, investigating how this binding could be done when we have to deal with an Adapter is a challenge we are working on right now.
- Authentication : There is a limitation on the part of the Github API where the same IP can not perform more than a certain number of calls (https://api.github.com/rate\_limit) without authenticating a user; so adding a basic authentication with OAuth2 through the Github website would be a good point to start improving this application
- Pagination : Currently no paging mechanism has been implemented, so the application does not show more than a certain number of results for a search, so implementing a paging mechanism could be an interesting challenge within this architecture.
If you are interested in knowing more about Pure Model-View-Presenter, I highly recommend you to subscribe to our monthly newsletter by clicking here.