Android Application Architecture ... The Right Way?
- Transfer
From a translator: Some of the terms that the author uses do not have a generally accepted translation (well, or I don’t know him :), so I decided to leave the majority in the original language - they are still understandable for those who write under android, but don’t know English.
Where to write about errors and inaccuracies, you know.
Over the past few months, as well as after discussions on Tuenti with colleagues like @pedro_g_s and @ flipper83 (by the way, 2 cool Android developers), I decided that it makes sense to write a note on the design of Android applications.
The purpose of the post is to talk a little about the design approach that I have been promoting in the last few months, and also to share all that I learned during the research and implementation of this approach.
We know that writing high-quality software is a complex and multifaceted task : the program must not only meet the established requirements, but also be reliable, easy to maintain, testable and flexible enough to add or change functions. And here comes the concept of “slender architecture” , which would be nice to keep in mind when developing any application.
The idea is simple: harmonious architecture is based on a group of methods that implement systems that are:

There need not be 4 circles (as in the diagram), this is just a diagram; It is important to consider the Rule of Dependencies : the code should have dependencies only in the inner circles and should not have any idea what is happening in the outer circles.
Here is a short glossary of terms needed to better understand this approach:
I started with a simple one to understand how things are: I created a simple application that displays a list of friends that it receives from the cloud, and when I click on a friend, it displays more detailed information about it on a new screen.
I will leave a video here for you to understand what is being discussed:
Our goal is to separate tasks in such a way that business logic does not know anything about the outside world, so that it can be tested without any dependencies and external elements.
To achieve this, I propose to break the project into 3 layers , each of which has its own goal and can work independently of the others.
It is worth noting that each layer uses its own data model, so you can achieve the necessary independence (you can see in the code that you need a data mapper to perform data transformation. This is a forced fee to ensure that the models within the application do not overlap). Here is a diagram of how it looks:

Note:I did not use external libraries (except for gson for parsing json and junit, mockito, robolectric and espresso for testing) so that the example would be more visual. In any case, do not hesitate to use ORM to store information or any dependency injection framework, and indeed tools or libraries that make your life easier (remember: reinventing the wheel is not a good idea).
Here the logic is associated with Views and animations occur. This is nothing more than a Model View Presenter (i.e., MVP ), but you can use any other pattern like MVC or MVVM. I will not go into details, but fragments and activities are just views, there is no logic there other than the UI logic and rendering of this display itself. Presenters on this layer communicate with interactors , which means working in a new stream (not in a UI stream), and transmitting through the callbacks the information that will be displayed in the view.

If you want to see a cool example of an effective Android UI that uses MVP and MVVM, take a look at an example implementation from my friend Pedro Gómez.
All logic is implemented in this layer. Considering the project, you will see here the implementation of interactors (Use Cases - methods of use).
This layer is a pure Java module without any Android dependencies. All external components use interfaces to communicate with business objects.

All data needed for the application is delivered from this layer through the implementation of UserRepository (the interface is in the domain layer - the business logic layer), which uses the Repository Pattern with a strategy that, through the factory, selects various data sources, depending on certain conditions.
For example, to obtain a specific user by id, the data source selects the disk cache if the user is already loaded into it, otherwise the request is sent to the cloud to receive data for further storage in the same cache.
The idea of all this is that the origin of the data is understandable for the client who does not care, the data comes from memory, cache or the cloud, it only matters to him that the data will be received and available.

Note: as for the code, remembering that the code is a training example, I implemented a very simple, even primitive cache, using shared shared preferences. Remember: DO NOT INVENT BIKES if there are libraries that solve the problem well.
This is a big topic that always has something to discuss (and here the author suggests sharing his decisions). As for my implementation, I used callbacks , which, in case something happens, say, in the data warehouse, have 2 methods: onResponse () and onError () . The latter encapsulates exceptions in a wrapper class called “ErrorBundle”: This approach is fraught with some difficulties because there are callback chains one by one until the error reaches the presentation layer to display. The readability of the code may be a bit broken because of this.
On the other hand, I implemented an event bus system that throws events if something is wrong, but such a solution is similar to using GOTO , and, in my opinion, sometimes, if you do not manage events very carefully, you can get lost in the chain of events, especially when several of them rush at the same time.
As for testing, I applied several solutions, depending on the layer.
I think in this place you are interested in looking at the code. Well, here is a link to github where you can find out what I did. What is worth mentioning about the folder structure is that different layers are represented by different modules:
Uncle Bob said, “Architecture is intent, not framework,” and I totally agree with him. Of course, there are different ways to implement any thing, and I’m sure that you, like me, face difficulties every day in this area, but using these techniques, you can be sure that your application will:
In conclusion, I strongly recommend that you try these methods, look at the result and share your experience with regard to this as well as to any other approach that, according to your observations, works better: we are sure that continuous improvement is always useful and positive.
Where to write about errors and inaccuracies, you know.
Over the past few months, as well as after discussions on Tuenti with colleagues like @pedro_g_s and @ flipper83 (by the way, 2 cool Android developers), I decided that it makes sense to write a note on the design of Android applications.
The purpose of the post is to talk a little about the design approach that I have been promoting in the last few months, and also to share all that I learned during the research and implementation of this approach.
Getting started
We know that writing high-quality software is a complex and multifaceted task : the program must not only meet the established requirements, but also be reliable, easy to maintain, testable and flexible enough to add or change functions. And here comes the concept of “slender architecture” , which would be nice to keep in mind when developing any application.
The idea is simple: harmonious architecture is based on a group of methods that implement systems that are:
- Independent of frameworks.
- Testable.
- Independent of the UI.
- Independent of the DB.
- Independent of any external service.

There need not be 4 circles (as in the diagram), this is just a diagram; It is important to consider the Rule of Dependencies : the code should have dependencies only in the inner circles and should not have any idea what is happening in the outer circles.
Here is a short glossary of terms needed to better understand this approach:
- Entities: This is the business logic of the application.
- Use Cases: These methods organize the flow of data into and out of Entities. They are also called Interactors.
- Interface Adapters: This set of adapters converts data from a format convenient for Uses and Entities. These adapters include Presenters and Controllers.
- Frameworks and Drivers: where parts accumulate: UIs, tools, frameworks, databases, etc.
For a better understanding, refer to this article or this video .
Our script
I started with a simple one to understand how things are: I created a simple application that displays a list of friends that it receives from the cloud, and when I click on a friend, it displays more detailed information about it on a new screen.
I will leave a video here for you to understand what is being discussed:
Android architecture
Our goal is to separate tasks in such a way that business logic does not know anything about the outside world, so that it can be tested without any dependencies and external elements.
To achieve this, I propose to break the project into 3 layers , each of which has its own goal and can work independently of the others.
It is worth noting that each layer uses its own data model, so you can achieve the necessary independence (you can see in the code that you need a data mapper to perform data transformation. This is a forced fee to ensure that the models within the application do not overlap). Here is a diagram of how it looks:

Note:I did not use external libraries (except for gson for parsing json and junit, mockito, robolectric and espresso for testing) so that the example would be more visual. In any case, do not hesitate to use ORM to store information or any dependency injection framework, and indeed tools or libraries that make your life easier (remember: reinventing the wheel is not a good idea).
Presentation Layer
Here the logic is associated with Views and animations occur. This is nothing more than a Model View Presenter (i.e., MVP ), but you can use any other pattern like MVC or MVVM. I will not go into details, but fragments and activities are just views, there is no logic there other than the UI logic and rendering of this display itself. Presenters on this layer communicate with interactors , which means working in a new stream (not in a UI stream), and transmitting through the callbacks the information that will be displayed in the view.

If you want to see a cool example of an effective Android UI that uses MVP and MVVM, take a look at an example implementation from my friend Pedro Gómez.
Domain Layer (Business Layer)
All logic is implemented in this layer. Considering the project, you will see here the implementation of interactors (Use Cases - methods of use).
This layer is a pure Java module without any Android dependencies. All external components use interfaces to communicate with business objects.

Data Layer
All data needed for the application is delivered from this layer through the implementation of UserRepository (the interface is in the domain layer - the business logic layer), which uses the Repository Pattern with a strategy that, through the factory, selects various data sources, depending on certain conditions.
For example, to obtain a specific user by id, the data source selects the disk cache if the user is already loaded into it, otherwise the request is sent to the cloud to receive data for further storage in the same cache.
The idea of all this is that the origin of the data is understandable for the client who does not care, the data comes from memory, cache or the cloud, it only matters to him that the data will be received and available.

Note: as for the code, remembering that the code is a training example, I implemented a very simple, even primitive cache, using shared shared preferences. Remember: DO NOT INVENT BIKES if there are libraries that solve the problem well.
Error processing
This is a big topic that always has something to discuss (and here the author suggests sharing his decisions). As for my implementation, I used callbacks , which, in case something happens, say, in the data warehouse, have 2 methods: onResponse () and onError () . The latter encapsulates exceptions in a wrapper class called “ErrorBundle”: This approach is fraught with some difficulties because there are callback chains one by one until the error reaches the presentation layer to display. The readability of the code may be a bit broken because of this.
On the other hand, I implemented an event bus system that throws events if something is wrong, but such a solution is similar to using GOTO , and, in my opinion, sometimes, if you do not manage events very carefully, you can get lost in the chain of events, especially when several of them rush at the same time.
Testing
As for testing, I applied several solutions, depending on the layer.
- Presentation Layer : existing android tools and espresso for integration and functional testing.
- Domain Layer : JUnit + mockito were used for unit tests.
- Data Layer : Robolectric (since this layer has android dependencies) + junit + for integration and unit tests.
Show me the code
I think in this place you are interested in looking at the code. Well, here is a link to github where you can find out what I did. What is worth mentioning about the folder structure is that different layers are represented by different modules:
- presentation : this is the android module for presentation layer.
- domain : java module without android dependencies.
- data : android-module, where all the data comes from.
- data-test : tests for the data layer. Due to certain limitations, when using Robolectric I had to use a separate java module.
Conclusion
Uncle Bob said, “Architecture is intent, not framework,” and I totally agree with him. Of course, there are different ways to implement any thing, and I’m sure that you, like me, face difficulties every day in this area, but using these techniques, you can be sure that your application will:
- Easy to support.
- Easy to test.
- Make up a whole
- Being divided.
In conclusion, I strongly recommend that you try these methods, look at the result and share your experience with regard to this as well as to any other approach that, according to your observations, works better: we are sure that continuous improvement is always useful and positive.