MVVM with Clean Architecture

Hossein Abbasi
ProAndroidDev
Published in
8 min readNov 7, 2018

--

Image via desktopbackground.org

You’re reading this article cause you definitely heard about MVVM architecture in Android and Clean Architecture as a high-level guideline for creating layered architectures; But, you’re not sure which solution, sample, or articles is right or best!

Let me clear it here right now for you, there is no best solution for this; Everyone has his own idea about it, but at least we able to understand it as much as possible and implement it the best way possible.

I’ve read thousands(it’s just a metaphor :D) of articles, sample applications, and definitions and after this, maybe I can tell you this approach which I’m going to tell you is one of the bests, that doesn’t have some mistakes in which the other ones did!

Keep in mind I’m not going to tell every inch of definitions which you can find in other articles!

Before continuing to read this article, this is mandatory to know some concepts. So, please read the other articles about this subject on the internet!

Of course, reading some of those articles and tracing their sample projects is just for that to see how you can implement as bad practice or even in a wrong way!

Some of them are:

Now that you read those articles, and have an open mind,

The first project is the one that we’re talking about it on this article and the second project is a template project that gets improved time-to-time:

Let’s go!

dependencies.gradle file is created to separate dependencies in another file:

For the Kotlin DSL version, click here.

For the Groovy version, click here.

There are many benefits of using this approach in your apps. One of them is it’s separated! and everything is categorized from the beginning and it makes you(or your colleague) stay inside the structure; You’re not able to use something wrong in a wrong layer(for example using dependency injection library in Domain layer!)

Note that if you add something to thedependencies.gradle, it’s only dependencies definition. To actually compile it to your application you would have to add it into module-level dependencies block. Like in data.gradle file:

dependencies {
implementation dataDependencies.rxJava
...
implementation project(':domain')
}

Note: Remember to add apply from: 'dependencies.gradle' to your top-level build gradle file.

Domain layer

Entity:

I’ve preferred to put all of my data classes to one sealed class, called Entity. The advantage is you able to access it easily with just Entity.Album.

sealed class Entity {

data class Album(
val id: Long,
val title: String,
val userId: Long
) : Entity()
}

When I was read about Clean Architecture, I came across this discussion on StackExchange, that says:

Even if you need put an @Entity annotation of the ORM into your domain object, I'd argue that this does not establish a "mention" of the outer layer. Because annotations are just metadata, no code that isn't specifically looking for them will see them. And more importantly, nothing needs to change if you remove them or replace them with a different database's annotation.

Wrrrrong statement!

Welcome to the Room kingdom!

Image by fireproofgames.com

Use case:

About use cases, I think there’s is just one thing that worth remembering:

We do not expect changes in this layer to affect the entities. We also do not expect this layer to be affected by changes to externalities such as the database, the UI, or any of the common frameworks. This layer is isolated from such concerns.

We do, however, expect that changes to the operation of the application will affect the use-cases and therefore the software in this layer. If the details of a use-case change, then some code in this layer will certainly be affected.

With the above point and definition of the Dependency Rule, I’ve put use case abstractions and their implementations inside the Domain layer.

There is a point that not related just to the Domain layer, which is:

You may see there are some empty interfaces inside the project as a parent (for example BaseUseCase). Let’s clarify it once forever: I’ve created those empty abstractions just to satisfy D in S.O.L.I.D principles in the whole project, constantly; So, later if you were to change a lower class, you would not also have to edit the class which uses it and thus violates Open-close principle.

Transformer:

In the implementation of each use case, you’re seeing there are some transformer abstractions (FTransformerfor example) and their implementations (AsyncFTransformerfor example). I’ve made these classes not just to reduce boilerplate codes, and makes the code more readable.

By boilerplate codes, I was meant:

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

The main reason that I’ve made those classes is we must not to add a dependency to the Android in the Domain layer.

What dependency?! Be my guest and take another look at above snippet code.

Note: you may notice that why there is just a subscribeOn and not a observeOn! According to the below definition, I’ve put the responsibility of setting observeOn to the developer who uses it on the above layers.

By default, an Observable and the chain of operators that you apply to it will do its work, and will notify its observers, on the same thread on which its Subscribe method is called. The SubscribeOn operator changes this behavior by specifying a different Scheduler on which the Observable should operate. The ObserveOnoperator specifies a different Scheduler that the Observable will use to send notifications to its observers.

As shown in this illustration, the SubscribeOn operator designates which thread the Observable will begin operating on, no matter at what point in the chain of operators that operator is called. ObserveOn, on the other hand, affects the thread that the Observable will use below where that operator appears. For this reason, you may call ObserveOn multiple times at various points during the chain of Observable operators in order to change on which threads certain of those operators operate.

Anyway, apart from Clean Architecture aspects, I’ve written an extension class to make it easy to apply schedulers:

Repository:

I’ve just put the abstraction of the repository in the Domain layer(unlike use case that put both abstraction and its implementation in the Domain layer).

Cause we want to comply with the Dependency Rule, that says:

Nothing in an inner circle can know anything at all about something in an outer circle.

Image via fernandocejas.com

Data Layer

API:

I’ve put all of my APIs inside the api package, without creating sub packages for each subject; Cause:

  1. I had a few numbers of different APIs.
  2. I’ve put my DTOs inside each in related API class; So, unlike my DAO and Data classes which I couldn’t put them in one single class, from my point of view, this seemed to be futile to create sub-packages for API package.

Note: Keep in mind to put related APIs and then their DTOs in a single class. By related API, I mean the exact same resource on each endpoint:

Image via avaldes.com

DAO & Data:

For the Data layer, we defined DAO for each section(AlbumDao, UserDao, …).

On the other hand, as I mentioned earlier, because of Room annotation compile errors, I couldn’t put Data classes inside their DAO classes.

About BaseDao it’s really not necessary to have such this class. It depends on your query functions; Are they output the same types or not?! For example, if you have multiple selectAll that returns different types, and then you’re going to write the method for each one, you’re rushing to create a God Class!

GIF by giphy.com

Data Source:

This is where our data sources live, i.e. Network, Database, Shared Preferences, Android Account Manager and etc.

Note: this is where we’re going to map our DTO/Data into Entity. For example:

override fun getAlbums(page: Int): Single<List<Entity.Album>> = api.getAlbums(page)
.map { item -> item.map { it.map() } }

I’ve mapped list of Album DTOs to List of Album Entities.

Mapper:

I’ve written Kotlin Extensions to make it easier and more clear to map one object to another one, to able to pass it through boundaries.

Repository:

This is where we implement the repository abstractions located in the Domain layer. The input parameters are our data sources.

In these implementations, we wrap our data into ResultState:

Another good solution to wrap the data into ResultState and doing Transformation, is merging them together. How?

About the BaseRepositoryImpl class which made by our team at work, please read my colleague's article, Hadi Lashkari Ghouchani.

Presentation Layer

OperationLiveData:

This custom MutableLiveData take operation and post it just once; In our sample, get albums from the server. We don’t want to send a request to the server each time we set an observer on that LiveData. I’m talking about this line for example:

observe(viewModel.albumsLiveData(), ::showAlbums)

Dependency Injection:

One of the best articles about the latest approach in using Dagger 2 is this article by Mladen Rakonjac.

UI:

For navigating between fragments and host activities, I’m using Navigation from Android Architecture Components.

For adapter in my list, I’ve used the latest approach from Google; PagedListAdapter. And set my custom DiffCallback that bring this feature when there is a change from the server(add, remove, change) on items, I’m going to show them on the list, smoothly.

[Deprecated] For more information, please read this article from my colleague, Reza Bigdeli. [Deprecated]

For more information, please follow this codelab from Google.

As you see, I’ve created each fragment under separated subpackage under ui package. But, another approach is to create separated modules per your pages and link them to the presentation module.

If you did something like that, don’t forget to set org.gradle.parallel to true, in gradle.properties file.

One of the advantages of this approach is each sub presentation module has its own resource folder; Something that has never been seen on Android. And of course, each submodule has it’s own tests too.

Update 01/2019

Some people asked me why the presentation layer has access to the Data layer.

To solve that, I’ve updated the sample project in this way:

  • Create an app module and move all of the DI packages from the presentation layer. This module has access to all the other layers.
  • Change the type of presentation layer to an Android Library.
  • Separate HomeModule class into three different classes; which are: HomeFragmentModule, HomeModule, and ViewModelModule.
  • And finally, remove implementation project(':data') line in presentation build.gradle file.

In the end, thanks for reading. Hope you enjoyed it. If you have any question or idea, feel free to comment on it.

--

--