As an Android developer, I spend a lot of my time worrying about app architecture.
Android has a long history of attempts at a convention. We’ve had Activity god classes. We then had fragment god classes. Along came MVP (Model-View-Presenter) with its presenters, which really improved things, but often led to god presenter classes.
More recently, Google introduced the Android Architecture Components, and we started using MVVM (Model-View-ViewModel). Somewhere along the transition from presenters to view models, I started seeing attempts at implementing clean architecture.
The problem with clean architecture is, it’s a bit of an abstract concept. If you read the Clean Architecture book, you are left with a vague notion of how a clean architecture would be implemented.
So it took a few iterations until my colleagues and I finally found a consistent architecture that works.
And this, very briefly, is how I now implement a clean architecture feature in an Android App:
I start with the domain. This is where my use cases are, as well as my domain models and my repository interfaces. Because it should not be coupled to anything in data or the UI, my domain layer is very straightforward. Repository interfaces are designed ignoring any APIs I would need entirely. They describe precisely what I need to present something to the user. My use case output is as simple: it defines all the data that should be presented to the user.
I wrap it up by stubbing my repositories, so that my upcoming presentation could start utilising the domain layer.
Once the domain is in place, I can proceed to my data and presentation layers.
The data layer contains repository implementations, data sources and mappers from domain to data and from data to data sources (APIs, databases, etc.) and vice versa.
I start by implementing my repositories. These are the entities that would eventually grab me the data I need and persist the data I need persisted.
The way repositories achieve this is by using data sources. Each data source abstracts a single source of data: an API, a local store or the Android API, for example. Since repositories should not be coupled to APIs either, their models reflect what the domain needs rather than what an API would provide. They mostly “speak” using data models. These are mapped from and to domain or data sources.
To wrap up my data implementation, I implement my data sources. The mappers from the data sources to the data is usually where most of the data massaging happens.
On the presentation layer I have my ViewModels and mappers to and from domain. This is where my presentation logic lives. It does not know about the UI. I usually like to imagine the UI as being a terminal, and then see if my implementation still makes sense. The ViewModel exposes function to the UI the describe the events presentation cares about. It also executes use cases and updates the view state and navigation.
Lastly, I have the UI. The UI layer is where my application, activities, fragments, custom views and mappers to and from presentation live. The fragments, activities and custom views can all act as views in the MVVM sense. They can all own ViewModels. They communicate with their ViewModels by calling the event functions. They observe the ViewModels for view state and navigation.
Everything is tied together using DI (Dependency Injection) in my main module. I use Dagger and Hilt, having had poor experience with Koin. This is what Clean Architecture refers to as the “dirty main”.
I tried to keep this explanation short. Clean Architecture is a vast topic, and its application to Android is quite complex as well. I hope this helps somebody!