Android, MVVM with Clean Code
A couple of weeks ago I did an interview for a company that asked me to create a currency converter using Kotlin, as I am quite happy with the final result I decided to post the project on my github and describe the architectural choices here (I think there aren’t too many updated samples on projects using MVVM-Clean Code architectural patterns in a real scenario).
Keep in mind that the project is the result of specific exercise requirements, some choices may seem strange, for example a specific requirement was to retrieve and save the dollar only rate, and calculate the exchange rate using an algorithm.
The project uses the openexchangerate API to retrieve the exchange rate, if you want to use a different key (or the one inside the project has expired), take a look at
app/build.gradle, you can change the value there.
In this project you’ll find
- MVVM and Clean Code architectural pattern, using views-> viewmodels -> usecases -> repositories
- Dependency injection using Hilt
- Room for persistence
- Retrofit for networking
- Coroutines for asynchronous operations
- LiveData and ui states handling for loading/success/error
- Unit testing
Why MVVM with Clean Code?
MVVM is great in android development, it helps separate the views from the business logic (preventing the problem of filling Fragments and Activities with business logic). A disadvantage of MVVM is that in large projects the ViewModel is overloaded with business logic, in such cases MVVM with the Clean Code principles can help to separate responsibilities and keep the project solid and easy to evolve and maintain.
Who is this project for?
- Anyone looking for a way to structure a robust android application that is easy to evolve and maintain
- A quick reference for a MVVM with Clean Code
What is it not?
- A UI/Material Design sample. The interface of the app is deliberately kept simple to focus on architecture.
- A Jetpack Compose sample. I feel more confident using XML layouts and I believe Compose is not mature enough to replace them.
- A real production app, consider it just a showcase of an updated MVVM sample using Clean Code and some cool (jetpack) libraries.
The project in detail
The project can be divided into three layers:
The presentation layer contains the View and ViewModel objects from MVVM. The views are Activities or Fragments, they must be as simple as possible, if you have any business logic inside the views you need to refactor these classes.
In the sample app the view is the represented by the CurrenciesFragment, this class has only two responsabilities: listen to the user interaction and reflect the changes to the model on UI.
This is a simpler version to show the fragment’s responsabilities:
ViewModel is a class that is responsible for preparing and managing the data for an Activity or a Fragment. It also handles the communication of the Activity/Fragment with the rest of the application (e.g. calling the business logic classes).
In an Android applicaiton the ViewModel can be implemented using the ViewModel Jetpack Library, in our sample app the ViewModel uses a State pattern, an object responsable to hold a value with its ui state (loading, success, error, empty.)
This is simpler version of app ViewModel and State classes:
The Domain Layer
The domain layer contains all the use cases of your application. You can consider the use cases as class responsible of containing the business logic of your application.
In the currency converter project you will find a single class, but keep in mind that a single class should be responsible for a single functionality, so don’t hesitate to create multiple use case classes if your app has different functionality.
Here’s a simpler version of CurrenciesUseCases class:
The Data Layer
The data layer contains the repositories used by the domain layer and the data sources.
In the sample app there are 2 repositories: LocalCurrencyRepository and HttpCurrencyRepository, in a bigger project you should create a repository for each type of data.
Here a simples version of Local and Remote repositories used by the app:
Other important features you can find in the project
LiveData is an observable data holder class. The main feature of LiveData is that it’s lifecycle-aware, so you don’t have to worry on what to do during onPause/onStop in order to avoid battery drain or other issues. The LiveData feature is perfect in combo with ViewModel (both are Jetpack libraries). In the sample app you can see LiveData is used within the ViewModel and it’s observed by the fragment in order to update the UI when the data changes.
If you don’t know what dependency injection is, please take a look here. For many years i used dependency injection libraries as little as possible because I didn’t like the implementations available in Android environments compared to other solutions (e.g. the wizardry of Spring dependency injection). Nowadays I appreciate Hilt and I happen to use it more and more frequently in my projects.
In the app, HILT is used to inject UseCases, Repositories, and Dao. Take a look at
di package to understand how the objects are provided.
What’s left out?
I am aware I left out some important parts of the project, but I preferred to focus on the architectural parts. In particular, there’s Room configuration and entities, custom exceptions used to handle specific errors, adapters, coroutines, and definitely something else.
If you have questions or doubts regarding the project send me an email or write on my socials.
The source code
You can find the source code here : github.com/nicolacaferra/architecture_sample_currency_converter