Once we have decided the features we want to have in our Minimum Viable Product version of the app, and before jumping straight away to start coding, we need to decide how are we going to design and architect the application.
Why we need to implement a good architecture? Because we want the code base of our application to be testable, scalable, robust, modular and maintainable, among other things.
Over the years a few architectures patterns has been defined and used by Software engineers for different platforms and systems but one of them that has gained lot of traction and popularity in the mobile community is the well known Clean Architecture.
What is Clean Architecture?
This pattern was defined by Uncle Bob and described in this blog post.
He summarise the main goals of a good Software Architecture:
- Independent of Frameworks: We want to isolate the Android or iOS framework from our business / UI logic, making them testable and interchangeable.
- Testable: This has been one of the traditional issues when designing the testing strategy of our apps, it was difficult to test a module that has many external dependencies, like the Android SDK.
- Independent of UI: We want to have our UI independent from our business logic, this way the last one can be unit tested without having to depend on UI or Integration tests.
- Independent of Database: The source of data must be isolated from the rest of layers of the app, this way we can change this source without affecting the logic of the app.
- Independent of any external agency: The business logic doesn’t depend on any external factor.
An Android approach to Clean Architecture
We are going to identify three main layers in the code base: Presentation layer, Domain layer and Data layer.
We are going to use the Model-View-Presenter (MVP) pattern to implement this layer. I will explain briefly this presentation pattern but you can see a more detailed explanation in my introductory article here.
Basically we are going to have a View entity, where we implement the Activities, Fragments and Views. These are what we call passive views, what means that they are totally dumb and have zero presentation or business logic. They just display information in the screen under the direction or the Presenter and dispatch events back to this one, like user actions.
Then we have the Presenter that as mentioned previously contains all the presentation logic and is acting as a bridge between the View and the Model layer. This entity should not have any Android dependency, but it knows how to respond to user actions and how to retrieve the information or data that is required to display in the view.
And finally we will have the Model layer that will contain the bridge and adapter classes accessing the Domain layer of our architecture.
It’s going to contain all the business logic of our app. Once again there shouldn’t be any Android dependency in this layer. In here we will implement what we call Interactors, which basically are Use Cases.
For example, if we look into the MadridBus application, we will see that one use case is to get the list and information of all the bus lines in the city. This logic will be implemented in one Interactor and it will be used by the Presenter of the Line List View.
So when the user open the line list screen, the View is going to tell the Presenter that it has been loaded, the Presenter knows then knows that have to retrieve the line list, so it’s going to instantiate and use the GetLineListInteractor, which is going to access the Data Layer to get the list of lines. When the data set is returned the Presenter knows calls the View and tell it to display this information in the right way.
In this layer is where all the data of the application is stored and processed. This layer is responsible of providing the mechanisms to retrieve the data that the app needs. The Interactors will be calling the API of the data layer so they can retrieve this data to the Presenters.
We want to make the data source totally agnostic for the rest of the different layers of the app. This means that the Interactors doesn’t know from where the Data layer is extracting the information, they will just call the interface of the API exported following the Repository Pattern.
For example, in the app we are going to have two sources of data, the first one will be the Cloud, from the public transport agency data service. Then we will have the local database, where we will save the data taken from the cloud, so the next time when the GetLineListInteractor call the Repository to get the list of lines, if this exist in the local database, it will retrieve from there, and if not it will retrieve it from the cloud.
To read more about the repository pattern check out this great blog post from Hannes Dorfmann.
The project structure
One importante aspect when designing our code base which is dependent on the architecture of the app is the structure of the different modules and how are they grouped in the project.
As Uncle Bob says, this is like a blue print of the application. Looking at the project structure should give us an idea of what this system is about.
As we can see in the screen shot, the project has four main module groups: DI, Domain, Model and UI.
In DI we have all Dagger 2 dependency injections helpers.
Domain as we have mentioned before contains the business logic, interactors, network modules, rules, etc.
In Model basically we have Pojos and data classes.
Finally in UI we have all the Presentation layer modules, like Activities, Presenters, etc.
With this project structure we can easily find and navigate to a specific feature or functionality of the app, either the UI part, the Domain or the Data component of it.
A good architecture is the blueprint for our project, it will guide our implementation and will keep the maintainability, testability, cohesion and modularity of the project.
Clean Architecture is not the silver bullet nor the only way to design our apps, but has proved to be one of the best solutions.
We will be following this pattern in the MadridBus source code implementation that you check out in Github here.