📜 ⬆️ ⬇️

Android application architecture ... The right way?

From the translator: Some 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 clear for those who write under android, but do not know English.
Where to write about errors and inaccuracies, you know.

Over the past few months, as well as after discussions with 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 about designing Android applications.

The purpose of the post is to tell a little about the design approach that I have been promoting in the past few months, and also to share everything 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, maintainable, testable and flexible enough to add or change functions. And here comes the concept of “slim architecture” , which would be nice to keep in mind when developing any application.
')
The idea is simple: a slender architecture is based on a group of methods implementing systems that are:



Pie chart

Circles do not have to be 4 (as in the diagram), it’s just a diagram; It is important to take into account the Rule of Dependencies : the code should have dependencies only in the inner circles and should not have any idea what happens in the outer circles.

Here is a small glossary of terms necessary for a better understanding of this approach:



Our script


I began with a simple, to understand how things are: I created a simple application that displays a list of friends that gets from the cloud, and when I click on a friend, it displays more detailed information about it on the new screen.
I will leave a video here for you to understand what is being said:



Android architecture


Our goal is to divide the tasks in such a way that the 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 split the project into 3 layers , each of which has its own purpose and can work independently of the others.

It is worth noting that each layer uses its own data model, thus you can achieve the necessary independence (you can see in the code that a data mapper is needed to perform data transformation. This is a forced charge for not intersecting the models inside the application). Here is a diagram of what it looks like:

3 layers

Note: I did not use external libraries (except for gson for parsing json and junit, mockito, robolectric and espresso for testing) in order to make the example more visual. In any case, feel free to use the ORM to store information or any dependency injection framework, and indeed tools or libraries that make your life easier (remember: reinventing the wheel again is not a good idea).

Presentation Layer (Presentation Layer)


Here logic is associated with Views and animations occur. This is nothing but the Model View Presenter (that is, 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 but the UI logic and the rendering of this display itself. Presenters on this layer are associated with interactors (intermediaries) , which implies working in a new stream (not in a UI stream), and passing through callbacks of information that will be displayed in the view.

image

If you want to see a cool example of an effective Android UI that uses MVP and MVVM, take a look at the implementation example from my friend Pedro GĂłmez.

Domain Layer (Business Logic Layer)


All logic is implemented in this layer. Looking at the project, you will see the implementation of interactors here (Use Cases - methods of use).

This layer is a module in pure Java without any Android dependencies. All external components use interfaces to communicate with business objects.

domain layer

Data Layer (Data Layer)


All the data needed for the application comes from this layer through the implementation of the UserRepository (the interface is in the domain layer - 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 disk cache is selected by the data source if the user is already loaded into it, otherwise a 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, data comes from memory, cache or the cloud, it is only important that the data will be received and available.

repository implementation

Note: as for the code, remembering that the code is a learning example, I implemented a very simple, even primitive cache using shared preferences storage. Remember: DO NOT INVENT A BIKE if there are libraries that solve the problem well.

Error processing


This is a big topic, in which there is always something to discuss (and here the author suggests sharing his decisions). As for my implementation, I used callbacks that, in case something happens, say, in the data store, have 2 methods: onResponse () and onError () . The latter encapsulates exceptions in a wrapper class called “ErrorBundle”: This approach presents some difficulties because there are chains of callbacks one after another until the error goes to the presentation layer to be displayed. The readability of the code may be slightly impaired due to this.

On the other hand, I implemented the event bus system, which throws events, if something is wrong, but this solution is similar to using GOTO , and, in my opinion, sometimes, if you don’t manage events carefully, you can get lost in the chain of events, especially when there are several of them at the same time.

Testing


As for testing, I applied several solutions, depending on the layer.



Show me the code


I think in this place you are interested to look at the code. Well, here's a link to github , where you can find what I did. What is worth mentioning about the folder structure is that different layers are represented by different modules:



Conclusion


Uncle Bob said: “Architecture is intentions, not frameworks,” and I completely agree with him. Of course, there are different ways to implement any thing, and I am sure that you, like me, face difficulties in this area every day, 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 both with regard to this and any other approach that, according to your observations, works better: we are confident that continuous improvement is always beneficial and positive.

Links and sources


  1. Code: https://github.com/android10/Android-CleanArchitecture
  2. The clean architecture from Uncle Bob
  3. Architecture is intentions, not frameworks.
  4. Model View Presenter
  5. Repository Pattern by Martin Fowler
  6. Presentation on Android Design Patterns

Source: https://habr.com/ru/post/250659/


All Articles