Android Clean App Base Library (Clean Architecture + MVVM) - Part I

Alperen Babagil
Huawei Developers
Published in
7 min readNov 26, 2020
Clean App Base

Hello everyone 🙋‍♂️ In this medium series I will talk about Clean Architecture in Android Development. Then I will explain my library: a base library for clean architecture projects. Finally I will show my tech stack that I used in my library and demo app that uses that library. Library and demo app is hosted on my github:

It is written with Kotlin in modular way. So you can use it some of its layers or all layers at once.

List of my tech stack: Kotlin, Clean Architecture, MVVM, Kotlin Coroutines, Repository Pattern, Dependency Injection with Koin, Data Binding, Unit testing up to Activity/Fragment with MockK, Coil for image loading, Retrofit2, Elements library for using RecyclerView.

What is Clean Architecture?

Clean Architecture is a software design concept proposed by Robert C. Martin and promoted on his blog, Uncle Bob. It says an application must meet customer’s needs with providing flexibility, testability, and maintainability. It divides software into layers with defining rules about communication between them. It is the famous chart of it:

The Clean Code Blog by Robert C.Martin (Uncle Bob)

According to this chart a circle must only know the next inner circle. Data flow must be inner circle to outer. Basic data interaction and flow must like this:

1- Presentation triggered by Framework & Drivers layer. That can be user input, push notification, DB change callback etc.

2- Presentation layer executes Use case. This layer also determines life cycle of execution.

3- Use case must ask Entity layer for data and wait for the result

4- Entity provides data source. At this point the entity is independent from application logic and works with enterprise logic.

5- After attempt to getting data Entity layer must return data or fail reason to Use case layer.

6- Use case must return this data and data state to Presentation layer. After this point Presentation has nothing to do with inner the circle.

7- Presentation layer uses data to inform the user and waits for other triggers.

Should I use it?

According to general opinion, it must be used at large and medium scale projects. For small scale projects it is considered as an overkill. But in my opinion it must be the default project structure for all projects. It provides excellent testability for apps. Besides it defines your classes and their responsibilities. So another developer easily understands which class does what. Additionally, all small scale projects evolve to medium as I observed. So:

In Android World

In Android we have Activities, Fragments, Intents, Services, Threads and Lifecycles. All components that we want to run away because these components are mostly system related and have unpredictable behaviours. Because mobile platform is a living thing and affected by the outside world. So we must create our clean architecture components such as layers, use cases, rules, entities etc. Additionally all program logic must not live in Activities/Fragments. It creates god objects that are hard to test and maintain.

Our structure is divided as 3 layers in Android: Data, Domain and Presentation. You can implement it as 3 main modules and separate futures in this module. Or you can divide your app into feature modules and create data-domain-presentation packages/modules in it. It is up to you. Below I will try to explain these layers with more familiar classes.

1- Domain Layer

Domain layer is the center of our structure, it connects the data and presentation layer. Ina a modular way data only knows domain layer same as presentation. Because data layer is irrelevant and replaceable for our app, it must work with app models. It contains general a stateful data container model (DataHolder) known by all layers and Use cases (I will call it Interactor from now on). Interactors must have a single purpose: get one type of data from data layer and pass it to presentation layer. If execution needs parameter(s) it must be special for that interactor so changes at data or presentation must affect maximum 2 layers.

Example DataHolder

In my way I don’t use any threading logic and keep interactors as simple as possible. They are more like defined rules about getting data with specified types. They consist of suspend functions that calls related repository (what the h*ll is a repository?)

Example Interactor

Repository has getting data logic and actually belongs to the data layer but it’s definition resides in domain layer because interactor must know it. I will talk about in data layer.

2- Data Layer

Data layer handles all enterprise logic and produces data for our app. All complex logic must belong here. It must also be as independent as possible from platform. So it can be unit tested and modifiable. It mainly consists of 3 parts: Data Source, Repository Implementation and Mapper.

Data source is responsible for getting one type of data. Does mostly the hard work of this layer. It can communicate with the server or do the text recognition etc.

Example Data Source

Repository Implementation is a type of repository which resides in domain layer. It’s duty is manage data sources and provide app objects to the interactor. It can use more than data source to get all data reordered joined and mapped to app models defined in domain layer with help of mappers.

Example Repository Implementation

Mappers are used to convert enterprise objects to app objects or app requests to enterprise requests. Let’s say app model to be sent to server has Date class. But server accept this field as a formatted string. Mapper makes possible this conversion.

3- Presentation Layer

Presentation layer is the most familiar layer to us, it contains activities, fragments and view models etc. This layer has two responsibilities: get triggered then request data and show data that comes from domain layer. Additionally this is the layer that system gives resources to run your app. Calls your lifecycle calbacks, broadcasts events etc. But has many states to be carefully managed. That means things can be get messy if code logic piled up here. Besides it makes impossible to unit test your code.

MVVM structure is a good way to make presentation layer reliable . Using view model is the key point. It reduces lifecycle effects and gives an coroutine scope easily (viewModelScope). In my design it executes interactors in desired coroutine scope and post it to a live data as DataHolder. Then this live data observed in activity/fragment and necessary UI operations performed according to it’s success/fail/loading state. By that way activity/fragment only knows and observes view model, that separates framework from inner circle.

Observing DataHolder Example
Executing Interactor Example

All threading logic handled in view model because it follows it’s presenter’s lifecycle. Usually we need data if we are looking related screen at that moment. When a screen finished related view model is closed and it cancels it’s data getting process automatically and safely.

Too many classes… How can I manage them?

As our app grows, number of instances and classes to get data increases. We create data sources, repositories, interactors, mappers… for every type of models. Instance creation and lifecycle management of this this classes requires huge amount of code and prone to generating bugs.

The answer is Dependency Injection. It creates object instances for you and keeps them in the memory as long as they are needed. Thanks to the DI, we put needed class interfaces into our constructor and just use it. By this way a class does only its job, nothing more (Separation of concerns). Because of the tools class needed comes from outside, unit testing our class as easy as shelling peas.

I usually use Koin for dependency injection. It is easy to set up and makes great job with little piece of code. Here is an example to one of my demo app’s modules.

This module creates all needed classes for my main data layer (I will explain this app in detail at next parts). UserRepositoryImpl needs GetUserDataDataSource. GetUserDataDataSource needs a UserService retrofit and an ApiCallAdapter. If you noticed there is no information about creation of ApiCallAdapter. It resides another module and because of modules are connected eventually, it can create ApiCallAdapter for all modules. This is how DI makes our life easier and reduces our boilerplate code.

That was the Part I. In part II, I will talk about Clean App Base library that I made to get rid of duplicate code for clean architecture projects. In part III & IV will explain Demo app that uses Clean App Base Library ,tools and unit testing.

Happy coding 🎉

Part II:

--

--

Huawei Developers
Huawei Developers

Published in Huawei Developers

As Huawei Developers, our Medium publication where we share information about the use of Huawei Ecosystem