Clean architecture is a topic that never gets old in the Android world, and from the comments and questions I receive, I feel it’s still not very clear.
I know there are tens (or probably hundreds) of articles related to clean architecture, but here I wanted to give a more pragmatic/simplistic approach that can help in the first incursion to the clean architecture. That’s why I’ll be omitting concepts that may feel unavoidable to architecture purists.
My only goal here is that you understand what I consider the main (and most complicated) topic in clean architecture: the dependency inversion. Once you get that, you can go to other articles to fill in the little gaps that I may have left outside.
Clean architecture: why should I care?
Even if you decide not to use architectures in your Apps, I think that learning them is really interesting, because they will help you understand important programming and OOP concepts.
Architectures allow decoupling different units of your code in an organized manner. That way the code gets easier to understand, modify and test.
But complex architectures, like the pure clean architecture, can also bring the opposite effect: decoupling your code also means creating lots of boundaries, models, data transformations… that may end up increasing the learning curve of your code to a point where it wouldn’t be worth it.
So, as you should do with everything you learn, try it in the real world and decide what level of complexity you want to introduce. It will depend on the team, the size of the App, the kind of problems it solves…
So let’s start! First, let’s define the layers that our App will use.
The layers for a clean architecture
You can see different approaches from different people. But for simplicity, we’re sticking to 5 layers (it’s complex enough anyway 😂):
1. Presentation
it’s the layer that interacts with the UI. You will probably see it divided into two layers in other examples, because you could technically extract everything but the framework classes to another layer. But in practice, it’s hardly ever useful and it complicates things.
This presentation layer usually consists of Android UI (activities, fragments, views) and presenters or view models, depending on the presentation pattern you decide to use. If you go for MVP, I have an article where I explain it in deep (and I would like to write one about MVVM soon).
2. Use cases
It’s usually called interactors too. These are mainly the actions that the user can trigger. Those can be active actions (the user clicks on a button) or implicit actions (the App navigates to a screen).
If you want to be extra-pragmatic, you can even avoid this layer. I like it because it’s usually the point where I switch threads. From this point, I can run everything else on a background thread and forget about being careful with the UI thread. I like it because I don’t need to wonder anymore if something is running in the UI thread or a background thread.
3. Domain
Also known as business logic. These are the rules of your business.
It contains all the business models. For instance, in a movies App, it could be the Movie
class, the Subtitle
class, etc.
Ideally, it should be the biggest layer, though it’s true that Android Apps usually tend to just draw an API in the screen of a phone, so most of the core logic will just consist of requesting and persisting data.
4. Data
In this layer, you have an abstract definition of the different data sources, and how they should be used. Here, you will normally use a repository pattern that, for a given request, it’s able to decide where to find the information.
In a typical App, you would save your data locally and recover it from the network. So this layer can check whether the data is in a local database. If it’s there and it’s not expired, return it as a result, and otherwise ask the API for it and save it locally.
But data not only comes from a request. You may, for instance, need data from the device sensors, or from a BroadcastReceiver
(though the data layer should never know about this concept! We’ll see it later)
5. Framework
You can find this layer called in many different ways. It basically encapsulates the interaction with the framework, so that the rest of the code can be agnostic and reusable in case you want to implement the same App in another platform (a real option nowadays with Kotlin multi-platform projects!). With framework I’m not only referring to the Android framework here, but to any external libraries that we want to able to change easily in the future.
For instance, if the data layer needs to persist something, here you could use Room to do it. Or if it needs to do a request, you would use Retrofit. Or it can access the sensors to request some info. Whatever you need!
This layer should be as simple as possible, as all the logic should be abstracted into the data layer.
Remember! These are the suggested layers, but some of them can be merged. You could even just have three layers: presentation – domain – framework. This probably can’t be strictly called clean architecture, but I honestly don’t care about namings. I’ll leave 5 layers because it helps me explain the next point, which is the important one.
Interaction between layers
So this is the hardest part to explain and understand. I’ll try to be as clear as possible because I think this is also the most important point if you want to understand the clean architecture. But feel free to write me if you don’t understand anything, and I’ll update this text.
When you think of a logical way of interaction, you’d say that the presentation uses the use cases layer, which then will use the domain to access the data layer, which will finally use the framework to get access to the requested data. Then this data flies back to the layer structure until it reaches the presentation layer, which updates the UI. This would be a simple graph of what’s happening:
As you can see, the two limits of the flow depend on the framework, so they require using the Android dependency, while the rest of the layers only require Kotlin. This is really interesting if you want to divide each layer into a separate submodule. If you were to reuse the same code for (let’s say) a Web App, you’d just need to reimplement the presentation and framework layers.
But don’t mix the flow of the App with the direction of the dependencies between layers. If you’ve read about clean architecture before, you probably saw this graph:
Which is a bit different from the previous image. Namings are also different, but I reformulate this in a minute. Basically, the clean architecture says that we have outer and inner layers, and that the inner layers shouldn’t know about the outer ones. This means that an outer class can have an explicit dependency from an inner class, but not the other way round.
Let’s recreate the above graph with our own layers:
So from the UI to the domain, everything is quite simple, right? The presentation layer has a Use Case dependency, and it’s able to call to start the flow. Then the Use Case has a dependency to the domain.
But the problems come we go from the inside to the outside. For instance, when the data layer needs something from the framework. As it’s an inner layer, the Data layer doesn’t know anything about the external layers, so how can it communicate with them?
Pay attention, here it comes an important concept.
Dependency Inversion Principle
If you have learned about SOLID principles, you may have read about Dependency Inversion. But, as with many of these concepts, it’s possible that you didn’t understand how to apply it. The dependency inversion is the “D” of SOLID, and this is what it states:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions.
So, honestly, this doesn’t say too much to me in my day to day work. But with our example, it’s easier to understand.
An example for dependency inversion
Let’s say we have a DataRepository
in the data layer that requires a RoomDatabase
to recover some persisted data. The first approach would be to have something like this. This is our RoomDatabase
:
class RoomDatabase { fun requestItems(): List<Item> { ... } }
And the DataRepository
would instantiate it and use it:
class DataRepository { private val roomDatabase = RoomDatabase() fun requestItems(): List<Item> { val items = roomDatabase.requestItems() ... return result } }
But this is not possible! The Data layer doesn’t know about the classes in Framework because it’s an inner layer.
So the first step is to do an inversion of control (don’t mix with dependency inversion, they’re not the same), which means that instead of instantiating the class ourselves, we let it be provided from the outside (through the constructor):
class DataRepository(private val roomDatabase: RoomDatabase) { fun requestItems(): List<Item> { val items = roomDatabase.requestItems() ... return result } }
Easy right? But here it is where we need to do the dependency inversion. Instead of depending on the specific implementation, we’re going to depend on an abstraction (an interface). So the data module will have the following interface:
interface DataPersistence { fun requestItems(): List<Item> }
Now the DataRepository
can just use the interface (which is in its same layer):
class DataRepository(private val dataPersistence: DataPersistence) { fun requestItems(): List<Item> { val items = dataPersistence.requestItems() ... return result } }
And as the framework layer can use the data layer, it can implement that interface:
class RoomDatabase : DataPersistence { override fun requestItems(): List<Item> { ... } }
The only remaining point would be how to provide the dependency to the DataRepository
. That’s done using dependency injection. The external layers will take care of it.
I won’t dive deep into dependency injection in this article, as I don’t want to add many complex concepts. I have a set of articles talking about it and about Dagger if you want to know more.
Building an example
First of all, you can find the whole example in this repository. I’ll be skipping some details, so be sure to go there, fork it and play with it.
So all this is really good, but you need to put it into practice if you want to understand it.
For that, we’re creating an App that will allow to request the current geolocation thanks to a button and keep a record of the previously requested locations, by showing them on a list.
Building a sample project
The project will consist of a set of 5 modules.
For simplicity, I’m only creating 4:
- app: it will be the only project that uses the Android framework, which will include the Presentation and Framework layers.
- usecases: it will be a Kotlin module (doesn’t need the Android framework).
- domain: another Kotlin module.
- data: a Kotlin module too.
You could perfectly do everything in the same module, and use packages. But it’s easier not to violate the dependency flow if you do it like that. So I recommend it if you’re starting.
Create a new project, which will automatically create the app
module, and then create the extra Java modules:
There is not a “Kotlin Library” option, so you will need to add the Kotlin support later.
The domain layer
We need a class that represents the location. On Android, we already have a Location class. But remember that we want to leave the implementation details in the outer layers.
Imagine that you want to use this code tomorrow for a web App written in KotlinJS. You won’t have access to the Android classes anymore.
So here it is the class:
data class Location(val latitude: Double, val longitude: Double, val date: Date)
If you read about this before, pure clean architecture would have one model representation per layer, which in our case would involve having a
Location
class on each layer. Then, you would use data transformations to convert it through different layers. That makes layers less coupled, but also everything more complex. In this example, I’ll just do it when it’s strictly required.
That’s probably all you need in this layer for this simple example.
The data layer
The data layer is usually modeled by using repositories that have access to the data we need. We can have something like this:
class LocationsRepository { fun getSavedLocations(): List<Location> { ... } fun requestNewLocation(): List<Location> { ... } }
It has a function to get the old requested locations, and another function to request a new one.
As you can see, this layer is using the domain layer. An outer layer can use the inner layers (but not the other way round). To do that, you need to add a new dependency to the module build.gradle
:
dependencies { implementation project(':domain') ... }
The repository is going to use a couple of sources:
class LocationsRepository( private val locationPersistenceSource: LocationPersistenceSource, private val deviceLocationSource: DeviceLocationSource )
One of them has access to the persisted locations, and the other to the device location.
And here it is where the dependency inversion magic happens. These two sources are interfaces:
interface LocationPersistenceSource { fun getPersistedLocations(): List<Location> fun saveNewLocation(location: Location) } interface DeviceLocationSource { fun getDeviceLocation(): Location }
And the data layer doesn’t know (and doesn’t need to know) what’s the real implementation of these interfaces. The persistence and device locations need to be managed by the specific device framework. Again, getting back to the KotlinJS example, a web App would implement this very differently from an Android App.
Now, the LocationsRepository
can use these sources without knowing about the final implementation:
fun getSavedLocations(): List<Location> = locationPersistenceSource.getPersistedLocations() fun requestNewLocation(): List<Location> { val newLocation = deviceLocationSource.getDeviceLocation() locationPersistenceSource.saveNewLocation(newLocation) return getSavedLocations() }
The Use Cases layer
This is usually a very simple layer, that just converts user actions into interactions with the rest of inner layers. In our case, we’re having a couple of use cases:
GetLocations
: it returns the locations that have already been recorded by the App.RequestNewLocation
: it will tell theLocationsRepository
to look for the current location
These use cases will get a dependency to the LocationRepository
:
class GetLocations(private val locationsRepository: LocationsRepository) { operator fun invoke(): List<Location> = locationsRepository.getSavedLocations() }
class RequestNewLocation(private val locationsRepository: LocationsRepository) { operator fun invoke(): List<Location> = locationsRepository.requestNewLocation() }
The framework layer
This one will be part of the app
module, and will mainly implement the dependencies that we provide to the rest of layers. In our particular case, it will be LocationPersistenceSource
and DeviceLocationSource
.
The first could be implemented with Room for instance, and the second with the LocationManager
. But, for the purpose of making this explanation simpler, I’ll be using fake implementations. I might do the real implementation at the end, but it would only add complexity to the explanation so I prefer you to forget about it for now.
For the persistence, I’m using a simple in-memory implementation:
class InMemoryLocationPersistenceSource : LocationPersistenceSource { private var locations: List<Location> = emptyList() override fun getPersistedLocations(): List<Location> = locations override fun saveNewLocation(location: Location) { locations += location } }
And a random generator for the other one:
class FakeLocationSource : DeviceLocationSource { private val random = Random(System.currentTimeMillis()) override fun getDeviceLocation(): Location = Location(random.nextDouble() * 180 - 90, random.nextDouble() * 360 - 180, Date()) }
Think about this in a real project. Thanks to the interfaces, during the implementation of a new feature, you can provide fake dependencies while working on the rest of the flow, and forget about the implementation details until the end.
This also proves that these implementation details are easily interchangeable. So maybe your App can work initially with an in-memory persistence, and then move to stored persistence. That can be implemented as we want, and then replaced.
Imagine that a new shiny library appears (like Room 😂) and you want to test it and consider about migrating. You just need to implement the interface using the new library, replace the dependency, and that’s it!
And of course, this also helps on tests, where we can replace those components by fake or mocked ones.
The presentation layer
And now we can write the UI. For this example, I’m using MVP, because this article was originated by the questions in my original article about MVP, and because I think that it’s easier to understand that using MVVM with architecture components. But both approaches are very similar.
First, we need to write the presenter, which will receive a View
dependency (the presenter interface to interact with its view) and the two use cases:
class MainPresenter( private var view: View?, private val getLocations: GetLocations, private val requestNewLocation: RequestNewLocation ) { interface View { fun renderLocations(locations: List<Location>) } fun onCreate() = launch(UI) { val locations = bg { getLocations() }.await() view?.renderLocations(locations) } fun newLocationClicked() = launch(UI) { val locations = bg { requestNewLocation() }.await() view?.renderLocations(locations) } fun onDestroy() { view = null } }
All quite simple here, apart from the way to do the background tasks. I’m using coroutines. I’m not sure if it’s the best decision, because I wanted to keep this example as easy as possible. So let me know in the comments if you don’t understand it. I already talked about Kotlin 1.3 coroutines on this blog if you are interested.
Finally, the MainActivity
. In order to avoid using a dependency injector, I declared the dependencies here:
private val presenter: MainPresenter init { // This would be done by a dependency injector in a complex App // val persistence = InMemoryLocationPersistenceSource() val deviceLocation = FakeLocationSource() val locationsRepository = LocationsRepository(persistence, deviceLocation) presenter = MainPresenter( this, GetLocations(locationsRepository), RequestNewLocation(locationsRepository) ) }
I wouldn’t recommend this for a big App, because you wouldn’t be able to replace the dependencies in UI tests for instance, but it’s enough for this example.
And the rest of the code doesn’t need much explanation. It has a RecyclerView
and a Button
. When the button is clicked, it calls to the presenter so that it requests a new location:
newLocationBtn.setOnClickListener { presenter.newLocationClicked() }
And when the presenter finishes, it calls the View
method. This interface is implemented by the activity this way:
override fun renderLocations(locations: List<Location>) { locationsAdapter.items = locations }
Layer models and data transformations
As I mentioned above, I don’t like creating models for all layers just by default. But to me, the presentation can be a good place where we could make use of this concept.
As you saw in the adapter code, I did some complex calculations to convert the domain model into what we want to see in the screen. That can be simplified by using a specific model:
data class Location(val coordinates: String, val date: String)
And then, we can convert the domain model into the presentation model. In this case, I’m using an extension function that applies to the domain Location
. Remember that, thanks to named imports, we can change the name of the classes when we have two that are called the same:
import com.antonioleiva.domain.Location as DomainLocation ... fun DomainLocation.toPresentationModel(): Location = Location( "${latitude.toPrettifiedString()} | ${longitude.toPrettifiedString()}", date.toPrettifiedString() )
After this, the transformation in the presenter is really easy:
view?.renderLocations(locations.map(DomainLocation::toPresentationModel))
And finally, the adapter looks much simpler:
fun bind(location: Location) { with(location) { locationCoordinates.text = coordinates locationDate.text = date } }
That way, we prevent from doing data transformations in the UI classes, which reduces the complexity.
Conclusion
So that’s it! The basics of clean architecture are in fact quite simple.
You only need to understand how the dependency inversion works, and then link the layers properly. Just remember not to add dependencies to the outer modules from the inner ones, and you will have an excellent help from the IDE to do things right.
I know this is a lot of information. But my goal is that this serves as an entry point for people that never saw clean architecture before. So if you still have doubts, please let me know in the comments and I’ll rewrite this article as many times as necessary.
And also remember that in order to make this article simple, I’ve omitted some complexities that you would find in a regular clean architecture. Once you have this settled, I suggest you read other more complete examples. I always like to recommend this one from Fernando Cejas.
The link to the Github repository is here. Go there to review the details, and if you like it, please show it with a star 🙂
Happy coding!
I dislike the concept of layers, in which a layer is allowed to depend upon a layer below it but not vice versa.
I prefer to have those “layers” independent from each other. A layer would connect to another layer only via interfaces, both directions.
Such a design would allow one to replace any layer without touching the other layers. Useful for unit tests and for creating a line of similar products.
That’s another approach, yeah. What I’ve found is that the inner layers usually don’t need interfaces because you can replace them with mocks in tests. It’s just a practical decision, and from a practical point of view, I’ve never required modifying them. I usually don’t need to change the classes in inner classes because they are the business core, so if it changes, it doesn’t make sense to keep the old one. It’s usually not the same with external dependencies. But that doesn’t mean that there aren’t any other great architectures. I’d love to see some code following your approach if you have something published.
Interfaces are unintuitive. Message bus (pub-sub) pattern is a lot better for your proposal.
The worst “solution” and pretty much always indicator that there is no architecture in mobile application at all is any type of message bus.
It is super easy to just put all the messages in the same bucket and others will take it from the same bucket, but pretty soon no one will know where is data coming from, who sent it, and debugging any issue becomes living hell. And that is even just scratching the surface of issues bus is bringing in practise. Not to mention components highly dependent on each other, impossible reusability because everyone is already using everyone, weird workaround for testing (if it is possible to test it at all), etc..
To put it simple it become very very fast big ball of mud that every developer is afraid to touch.
Yeah, it depends on how it’s used, but it’s a clear source of problems.
Hi Antonio,
Thank you for this article. It clarifies the dependency inversion principle very well. I have one question related to the DataPersistence interface, you mention “So the domain module will have the following interface”, this interface shouldn’t belong to the data module?
Thank you.
Correct! That’s a typo. Thanks, I’ll fix it
Hi,
First of all , you wrote a very good article on clean architecture. I have two queries regarding using clean architecture in Android applications.
1. Should we need to use clean architecture for every Android application. If answer is no , then what will be guideline for choosing android application for which we need to use clean architecture. I am asking this question because if we follow clean architecture then it required significant amount of coding effort to implement clean architecture.
2. Should we have data object for each layer with mapper? I seen article on clean architecture, which suggest we should use data object for each layer with mapper, which make each layer independent of other and also testable.
For first one, it’s a very personal question. I would use it always, but makes sense not using it if the project is small, has a fixed start and end with no future updates and clear features, and you’re not planning to write tests. You can use any flexible enough architecture though, not clean specifically.
For the second yeah, that’s what I mention in the article. It helps having more decoupled layers, but if the models are two similar between layers, I tend to avoid them. IMO the most important transformations happen in the external boundaries. For example when you parse a model from a Json, that model tends to have the info structured in an arbitrary way that is probably not what you need. And if the structure of the Json changes, it would affect your whole App. My advice is to create the ideal model in the domain layer, and use it where you can. If you need a different one in some layer (for example, in this sample it would make sense for the UI) you can create a new one.
I’ll probably change the sample code to use a different model for the presentation layer, because it makes sense in this case, as I’m transforming the data in the adapter.
One more question , Why you avoid using RxJava , instead you used Kotlin coroutines. I know it’s simple application, but what is your opinion for production projects. The main advantage I found from RxJava is that it provides operator which allow us to execute network request in parallel also allowing combining results of multiple network calls without writing extra code for these tasks. At the same time my data layer , domain layer, presentation layer have dependency on Rx Java.
That’s a similar question to one I asked on Twitter not long ago https://twitter.com/lime_cl/status/1039793278667960320?s=19 . It’s true that it couples your layers to Rx. But if you are really taking the most out of the library (more than just doing simple http requests) then it may make sense. I imagine that it’d be a problem if you want to reuse code in a Kotlin multi-platform project
What advantage does RxJava provide over Kotlin Coroutines?
Aren’t Use Cases part of the Domain layer (business logic)? Or do they have nothing to do with business logic…
If you see the classic clean architecture graph, they’re a layer that covers the “entities” (or domain) one, but it’s not part of it. That said, it doesn’t mean that you cannot merge those two layers. I can’t think right now on any drawbacks.
Doing a rudimentary search for Clean Architecture graphs, leaves one to think that there IS NO ONE way to define these layers: http://bit.ly/2P8NVa7
Yeah, I don’t think you need to be strict with the layers to consider it a clean architecture. Though the original one, the one that Uncle Bob shared, is the one that I put first. You can find the original article here https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
Antonio, excellent article. I agree with you, it depends on the solution the amount of layers and artifacts that are used. And the holy grail is to keep the “Dependency Inversion” between layers.
Great article! I really like the idea of putting framework classes like location behind a repository. For the UseCase layer however, it seems overkill to force all data calls to go through one method classes (I’ve seen this method create very complex code in bigger projects). I was wondering what your thoughts were on either combining use cases into objects of common behavior (e.g. the two location use cases here could be combined into one class of Location behaviour) or skipping the UseCase layer for calls that effectively delegate to other layers (e.g. giving the presenter access to the repository layer)
I totally agree. My only concern with using classes to combine several use cases is that those classes can grow indiscriminately. But if the class is refactored when that’s starting to happen, I think it is a good approach.
If you use the repository directly, when any complexity is added, for instance you need another module to check or validate something, that would require another refactored that is easy to ignore. If the team is disciplined enough, I don’t see it wrong either.
I think the important point is to define a set of rules that make all the team work the same way, so that the code is homogeneous. That way, when someone has to understand one part of the code they didn’t touch before, the cognitive load is small.
Agree with the repository point! You’d need a pretty disciplined team, willing to refactor as they go.
For usecases, I’d argue that the potential growth in one method classes is just as damaging as the potential growth in overly large classes. Imagine you have a large project with 50 api calls plus database, caching, etc. With one method classes, that could easily lead to 200+ UseCases, many of which use other usecases – eventually you end up with complexity through lack of context.
An idea for a more scalable structure could be usecases with names which define a general responsibility. i.e. UserUpdater, LocationHandler, Authenticator. Some of these would be one method classes, but most would be collections of behaviour, and by assigning each usecase a responsibility you ‘should’ avoid creating indiscriminate, large classes.
In the end, use cases are using the Command pattern, whose benefit is that the number of commands can grow without affecting the complexity of the code. If you manage to define classes with a reduced number of functions that won’t grow indiscriminately, then it sounds perfectly ok to me. In fact, I’ve seen it on some approaches to clean architecture.
It has another benefit: with uses cases as classes, a complex presenter will have many use cases, and constructors can become huge and messy.
Agree the one method classes can get unweidly, but there is advantages:
– Less chance of unnecessarily instatiating a class that is not used in the method you want to call (small memory saving).
– and more importantly, less issues with circular dependenciesdue to injecting classes that aren’t related to the function we need
Agreed!
Hey Antonio, awesome article.
One quick clarification on this your sentence – “And as the data layer can use the domain layer, it can implement that interface”
It seems to me that it should be “framework layer” rather than “data layer”, and “data layer” instead of “domain layer”, as I presume RoomDatabase is in the framework layer, and DataPersistence interface is in the data layer.
Hi Antonio, awesome article.
One quick clarification on this your statement – “And as the data layer can use the domain layer, it can implement that interface”
I believe it should be “framework layer” rather than “data layer”, and “data layer” instead of “domain layer”, as I presume RoomDatabase is in the framework layer and DataPersistence interface is in the data layer. Right?
Yup! That’s wrong, I’ll fix it. Thanks!
Hi Antonio,
I have query regarding repository, I have huge number of network calls in my application it keeps increasing size of repository class. Do you have any idea how we can refactor repository class or limit size of repository.
You don’t need to have just one repository for the whole App or the complete API. In fact, having separated repositories for different entities is a good thing to do. It’s difficult to tell you how to divide it without knowing more about the context. But for instance, if you have users and posts in your App, you could have a repository to deal with users, and another one to deal with posts.
Hi Antonio! Very nice article.
You wrote at the beginning about a case if the data comes from a Broadcast, but I can not find any more details about that case. Can you tell us how would you handle that? What about if you need to implement a ContactRepository that lists all the contacts from the device via LoaderManager? Would you pass the loader to the framework/data layer?
Thanks 🙂
I think it’s an interesting topic for another post, so I’ll really consider it. But to give an answer now, yeah. The framework layer would implement some kind of interface from the inner layer, and provide an implementation that interacts with the Android APIs
Hi Antonio, as always it’s an excellent article. I would like to ask you a question because I’m doing an app and I would like to separate it in these five modules (app, usecase, domain, data, framework) you explained. I have the domain module, entire written in Kotlin, and inside it I have one class that contains the logic and the state of a game. In which module should I maintain the instantiation of this class if I would like to follow the MVVM rules?
Thanks in advance!!
To me, it sounds like it would be in the domain layer. It’s the business logic.
As a 10 year web developer who has been studying Android developing for the last month I have found this post the best for understanding clean architecture. I love the pragmatic view you apply in your explanation. This for me is the true indicator that somebody understands what is behind a theory. Not just applying the theory but to twist it to your real needs. Thanks for sharing.
Just a minor typo. in the “Building a sample project section” it says “app: it will be the only project that uses” I guess it is meant to say “app: it will be the only module that uses…”
Thanks for your words! I’m so glad to hear that it reached my goal. And thanks for the typo heads-up, fixing it now.
Great artice. I have a question. if there is a non crud function (eg: call backend endpoint to compute something and send me back the response). does this fit with “use case -> repository -> source” or this is not repository job as it is not crud(get/update/delete) ?
The technical definition of repositories implies CRUD operations as you mention. But here, it’s usually a more flexible concept. As Christian Panadero mentioned in one of his articles, it probably shouldn’t be called like that: https://panavtec.me/what-is-the-repository-pattern-and-why-im-not-going-to-use-it-on-android
Hi Antonio, thanks for this great article.
I have been using Retrofit + ViewModel + Livedata for a while and I am interested in taking a step further using clean architecture. I am used to returning instead of a List of items, an object that holds the list with more information in order to be able to handle errors (These errors might be a network connection error or an unauthorized login attempt, for example).
How could I approach it? It seems to me that retrofit parsing model classes should be in the framework layer, however, I would need them in the Repository class in order to attach any errors to the response…
Thanks!
Ok, so you can have a model in the framework layer (the model that represent the Retrofit response), and then a repository model that meets your needs. When the framework communicates with the repository, it will need a class or interface that belongs to the repository (as the framework is an external layer), so before doing that call, you would convert the data to the format that the repository understands. Does it answer your question?
Thanks for your quick response. I still have a few doubts:
1- My concern is, is it wrong (from the clean architecture point of view) to add or to wrap the regular service/database responses with some basic information about errors?
2- Where would you locate that wrapper object (data or domain layer)?
3- When asking for data to the ViewModel from an activity, would you return in the same LiveData object any error related to the request, or would you consider defining a different LiveData for handling errors?
Thanks again!
Regarding 1, no. In fact it’s a quite common way to do it. Check for Either or Result classes (check this article, for instance: https://www.ibm.com/developerworks/library/j-ft13/index.html), and you’ll see that it’s pretty similar to what you’re suggesting.
Regarding 2, if you use Eiter, this can be anywhere, even in your most inner classes if you want, because it’s a standard concept. You can use a library for that (such as Arrow https://arrow-kt.io/docs/arrow/core/either/) or create your own.
Regarding 3, it depends on how you model your UI classes. It’s also common to have a parent activity (or fragment or whatever) that knows how to deal with errors that must be shown in the UI, for instance by showing a toast or a SnackBar. In general, I think that the same object is good enough, but if you see that you’re repeating code in different classes, try to find a cleaner solution.
One of the best summaries on Clean Architecture I read till now. One question, where would you keep the loaded items for a pagination list (each next page loads 30 more items)? Would you keep it in the ItemsRepository or in the Use Case / Interactor who is responsible for calling the ItemsRepository.getItems(offset: Int) and passing the list down to the REcyclerView – all works with LiveData or Rx?
Good question! Both things are good IMO, and I would go with one or the other depending on what you expect from the App. If the data is disposable (it doesn’t matter if it disappears when the activity dies), then it can be anywhere. Otherwise, it should be persisted and be part of the repository. If you want to be agnostic of that matter, leave it in the repository, and it can implement its own cache system, from just an array in memory to a database.
Was thinking the same. Conceptually it belongs to the Repo since in this case it would be a InMemoryDataSource, same way you have Db/RemoteDataSources. Thanks.
I think it’s okay to put a comment just saying “Thank you, for that great article.”
Of course! Thank you for your words!
Thanks for the article! Really helped me get into Clean Architecture. How do you expose states for your use cases? I did a sealed class that has Loading, Success, and Error. I feel like Loading shouldn’t be included in that Result class but I’m getting confused on where to transfer it.
Why would you think that loading should not be part of Result. I think it is the class name which is creating confusion. You can rename it to States.
I transferred the States into the presentation layer. The ViewModel handles the states depending on the UseCase executions while the Activity observes it and modifies the UI accordingly. I think the UseCase should just tell me if it’s successful or it has an error since while it’s not returning anything, it’s automatically loading.
Hi Antonio. The best practical explanation of clean architecture I’ve seen. I’m using basically this in my current project, with one exception. I don’t use an interface. Given there is 0 chance the code will be reused on another framework, and very low chance datasources will be swapped out, is there any practical disadvantage to ommitting interfaces. I just find it’s one less file to code, maintain and inject
if it’s related to the Android framework, there’s still value if you for instance want to mock it or to replace it during tests. If it’s an external library, there are more chances that it needs to be changed in the future.
Also, the Android framework can change from one version to the next one, so it’s always and useful lifeguard.
It’s much easier to mock interfaces in unit tests, then concrete classes.
So far this is the simplest explanation of Clean Architecture in Android I ever read.
Thanks!
Hi Antonio, thanks for the tutorial !!!!
I have one question though, let’s assume you are trying to check the network connectivity of the device in such a way that if the device is connected, the repository will fetch data from an API and if not it will fetch data from the database. How would you implement it in this context with this architecture ?
I would use a component in the repository, that would be an interface for that layer, that allows me to know the state of the network. Then the implementation would be in the framework layer.
Hi Antonio
i’m new to mvvm and clean architecture and this was so pragmatic article i’v ever seen over the internet and thank you for that!
but i have so many questions about this topic that i can’t solve them with reasoning after reading this article
like other framework components that doesn’t need data from remote or local
or is this graph proper for solving every problem in android or this is only for problems that using data from local or remote storages “https://antonioleiva.com/wp-content/uploads/2018/09/clean-architecture-interaction.png”?
for example implementing an application with MediaPlayer for playing sounds and where to add listeners and how to act with them?
could you please help me or introduce something that help me
thank you very much
It would be similar. What I would do is to abstract the media player by creating an interface in the Data Layer, whose implementation is in the Framework layer. That way you are not affected by changes in the Media Player API, and also you could convert those listeners into more useful things, like Observables if you use Rx, Flows if you use Coroutines, or LiveData if you want to stick to Android stuff… But if you’re not using any of those, just stick to the listener for now, and you’ll find ways to improve it in the future
Hi Antonio,
Bravo, you have done a great job. Thank you. I just have a question making me a bit confused. Actually in this article, you use Coroutine only on Presenter part and it seem very reasonable because it has nothing else than handling asynchronously tasks. But I’m using RxJava and I realize that I have to invoke it in different layers (DataLayer repository and UsesCase layer) because I also want to profit the “Reactive” part (means that updating instantly UI during the process that is still not completed). So how do you manage to limit the influence of RxJava (of course in case you use it)?
I don’t use RxJava, but it happens the same with coroutines. As they are more a programming style rather than a library itself, it’s impossible not to “pollute” the whole architecture with it. If you decide to use them, you need to assume that.
I try to keep the “usecases” as agnostic as possible and for that purpose I usually define the signatures of the ports with objects like “Any” and then do the casting to the object of the library in the element that has the dependency, for example in the repository or viewModel.
Keep in mind that “Any” represents an object of the core language.
Hello Antonio,
Great post and very helpful.
If you have a Room DB with a data class that contains things like androidx.room.Entity, androidx.room.PrimaryKey etc. how would you approach this? Make 2 POJO’s or violate the depency rule?
Thanks! I would make 2 POJO’s for that. In fact I always do it, because info usually comes from a server and needs to be stored in a DB and then sent to the UI. So I never rely on those two models (server and DB). I prefer to have my own domain model and rely on it for the whole App.
Hi Antonio,
Thank you for the wonderful post. Really helpful.
Thanks!
Very well explained. do you have a mvvm version of this tutorial?
Thanks! You have this: https://antonioleiva.com/mvvm-vs-mvp/
Thanks Antonio. The single best explanation on the whole of the web. I read Martin’s book and had lots of questions and you answered them all.
Wow, thanks a lot for that! It’s great to know that a managed to explain this topic correctly. It took me quite some time because it’s not easy… Thanks again!
Nice article. But would i approach the same concept in MVVM in the presentation layer
By following this article and the one about MVVM, I think it’s easy to follow. The architecture is exactly the same, only the presentation changes https://antonioleiva.com/mvvm-vs-mvp/
Hey Antonio!
Thanks for the article. This has been a joy to read.
I had one question though. I was looking at FakeLocationSource.java in the app’s framework package, and I see that Location is imported from the domain layer.
Just wondering, doesn’t this cause the framework layer to be dependent on the domain (center) layer? Is this where dependency injection with dagger comes in?
Any clarification on this would be awesome!
Eric
Thanks Eric! The external layers can depend on the internal ones. So the framework can see the domain, but no the other way round. You can’t use in the domain something from the framework layer. Does this answer your question?