Each developer has their own way of organizing a project by defining layers, modules, and protocols to ensure their architecture is easy to understand and will accommodate any upcoming changes. We know that there is no “silver bullet“, that is, a specific architecture that can be applied to any problem or application domain.
Even though there’s no silver bullet, I would like to share a little of what I consider a good model for starting an Android project from scratch in this blog post. The following is an architectural diagram, which defines the components I believe are fundamental in any Android project:
Photo: Own elaboration (2019).
Network can be considered responsible for any communication with the server. At this point in the project, we can raise two questions: “Will the application make any requests?” and “How is the communication with the server?”. If the app has no communication, you can remove this component from your architecture. Otherwise, you should check on how the communication with the server will be – if it’s something like REST, WebSocket, and so on.
Database defines the local storage of the app, and here we can reflect if the app will have any persistence. If so, we can decide which persistence mechanism to use. From the persistence available on the platform, we could choose between SharedPreferences or SQLite. If you need a relationship between stored data, I advise using SQLite, otherwise, you can use SharedPreferences, which will be faster to prototype. It is known that using SQLite directly can bring some difficulty and a lot of “boilerplate code”, so Android Jetpack provides the Room library to make our lives easier.
Repository provides an application-wide abstraction for data manipulation, so the other application modules do not need to know whether this data comes from the server or the database.
Domain defines our business rules, and it can be divided into model and useCases. The model is an organized translation of the data provided by the repository, defining the static structure of the app. Since useCases define actions on the model, these actions can be in the domain of the user or any agent of the application.
Presentation defines the entire graphical interface of the app: Activities, Fragments, Views, etc. I believe a good way to organize files within Presentation is to define one directory per app screen. So that the depth of these directories is defined by the flow that the user can perform in the app. For example, if screen “X” is accessed by screen “Y”, we might have a package within the screen “Y” package called screen “X” and its definitions.
In the diagram above I presented that each screen package has an Activity and a View Model. The View Model would be an abstraction of data to be presented in the respective Activity/Fragment or View. In this package, I can highlight three essential features: ViewModel, LiveData, and Data Binding. I am sure they will make your life easier, as they have done for me and for many other developers =D.
Finally, Core is a package that I usually put my extensions, utilities, application class, and dependency injection parts in. If you use Dagger to do that, I recommend looking into Koin, as it allows a less verbose injection of dependencies.
There is no “silver bullet”, but we know that a good project has its components/layers with well-defined responsibilities. We should define a communication protocol between these pieces, which must be respected so that we can establish an architecture. The more time we spend with that, less time we will spend on maintenance.
About the author
Gabriel Bronzatti Moro is a Mobile Software Engineer at Poatek. He has a Master’s Degree in Computer Science – Federal University of Rio Grande do Sul. Also, he has a Bachelor’s Degree in Software Engineering – Federal University of Pampa. He likes software development, gaucho culture, and music. He loves guinea pigs.