Clean architecture on Android
We all know that writing good software is tough and difficult, it’s not only about functional requirements, but also about robustness, maintainability, scalability and testability. In the android native team at eDreams ODIGEO, we researched about which architecture could be the best to solve our problems and to help us deliver quality software. Here is where the clean architecture comes into play, and it is:
- Independent of the framework
- Independent of the UI
- Independent of the database
- Independent of any external agency
As you can see in the picture there are four circles: each one of them represents a different software layer, but keep in mind that they are only schematic, and depending on your architectural decision, you might need more or less.
The fundamental rule that makes this architecture works, is the “Dependency rule” (arrows in the image). It’s an inward dependency and not outward, which means that the external layers knows about the inner ones, but the inner ones don’t know about the outer ones. To decouple client code from objects that needs to be changed, dependency injection is used; ideally a framework that takes care of this such as dagger 2 is perfect, but you could use the one that your are more familiar with.
Entities are the business objects of the application.
This layer contains and encapsulates all the use cases (business rules) of the system, also called interactors.
Here the data is transformed into the most convenient way for the uses cases. MVC, presenters and controllers belong to this layer.
Frameworks and drivers
This layer is where all the details go, UI, mobile and web frameworks, etc… The responsibility of the code here is mainly connecting to an inner layer.
For a more in-depth analysis I recommend you to check out this video.
To achieve clean architecture in android we decided to split the project into three different layers (presentation, domain and data) and have one module per layer, but it could be organized in packages or whatever it fits you.
The presentation layer contains everything related to views/animations and its logic. For this layer, the passive view pattern and model-view-presenter were the chosen ones, with 1:1 relationship between view a presenter. You could use the pattern that you feel more comfortable with, MVVM, MVP, MVC and Supervisor controller among others. The thing about passive view is that all the view logic is delegated to a controller or presenter, therefore you could swap views that implement the passive view contract with almost no effort and unit testing the controllers becomes direct.
This is a pure java layer with no additional dependencies. If you are working with android, the ideal thing is to do everything outside the main thread to avoid blocking the UI. A direct way to approach this is to use a ThreadPoolExecutor to abstract you away from all the asynchronous complexity. Also some people like to use Rxjava (a well-known accepted framework in the android community) instead, but be aware that you might be coupling the domain layer to an external dependency, though. In the end, you will have consider if rxjava is useful for you. Having this layer independent of framework will make your life easier to unit test the business logic.
In this layer, a variation of the the repository pattern without performing DSL queries is used in order to choose a specific data source (network, database, internal/external memory etc..) and abstract where the data is coming from. Additionally, caching strategy happens here and repositories live as singletons. You can read more about the evolution of this pattern on android in this link.
For example, let’s study a screen in our eDreams app. The last thing that the user has to do before book a flight is to introduce the credit card details.
When things start to get complicated, having just one view and one presenter is a little bit messy, so ideally there should be several views, each one of them with its own presenter and use cases respectively. With this approach, each view would be like a “black box” and you could reuse the entire view with its logic in another screen, or refactor the screen gradually without a need for rebuilding everything from scratch. Also, what is really useful about this technique is that if you have your contracts well defined: different developers could work in the same “main view” without running into many conflicts and develop it incrementally.
The following views and presenters can be identified:
Since payment presenter contains its children, the communication between them is direct. Other ways of communication that work are simple java listener or for those who use rxjava, a publish subject will do the job. The interesting thing about publish subject is that you could auto-update views simulating a “two-way data binding”, similar to the Angular JS concept.
When it comes to testing, following the dependency inversion principle is a great help because it allows you to depend on abstractions and not implementations, so mocks can be provided to tests double. Regarding frameworks, the best options for us are:
Presentation layer: Espresso for functional test and roboletric / junit with mockito for unit test.
Domain layer: This layer is pure java, so here junit and mockito for unit test works well.
Data layer: Junit and mockito for unit testing.