Better Programming

Advice for programmers.

Follow publication

Understanding Layered Architecture in KMM, Part 2 — Designing the Solution

Jose Flavio
Better Programming
Published in
5 min readApr 3, 2022
Final architecture design

Designing the solution

The application that we are going to develop is quite simple, but in order to understand all the features of separating the project into layers, we are going to make it complex, perhaps unnecessarily.

Our app will display a list of movies. That’s it. Just that. So we’re going to create layers and give each one responsibilities. I will list them in the order in which we are going to develop it:

Domain

Here we will create our main “Movie” model. In addition, the contract or definition of the repository and data sources.

The repository will be an interface inside a domain package and the classes for the data sources will be in another data package that we will see in the next chapter of the series (including the libraries that will help us save data to a local database and the one that will help us get data from a REST service).

Within our domain layer will be all the logic that we want our application to have regardless of the platform on which it will be implemented.

For example, do we want our app to display a list of movies? then we must create a class that is in charge of bringing us the movies. This class is what we will call “Use Case”, Use case or Interactor. It does not matter if you are programming the Android or iOS app, it will show a list of movies and that logic will be the same in both.

That means that our domain module must be able to “extract” itself from the Android app’s source code and be able to be used in another framework keeping the core functionality.

Perhaps it may not seem very useful to have to create a class that has only one function, and that the only function that it does is return a list of movies that is given to it by the repository. It is as if this use case is a handrail, a bridge.

But let’s see it this way: reading the following structure, what do you think this example app does?

Most likely it’s an app that has a shopping cart, right? In which you can see products, promotions, add them to the cart and make the purchase if all the data in the cart is validated.

When you enter a project and find something like this, you save a lot of time trying to understand how the app works. Of course, there may be use cases/interactors that perform more complex actions.

Our app will have only one use case implementation: GetMoviesInteractor. By the way, I prefer to use the term “interactor” when creating the use cases classes, because an Interactor is an object that implements a Use Case of the system.

Data

The data layer should be also the same for every framework or platform. It does not matter if the app runs on Android, iOS, web or desktop. The logic for the repositories and data sources implementation should be exactly the same.

However, we find a stopper here. Each platform has different ways to connect to REST APIs or to save data into a local database. Android mostly uses Retrofit and iOS apps use Alamofire. So, how we can handle a unique way to access the data even external or internal?

You can think of Retrofit since it works on any JVM project and the core of a KMM is a pure Kotlin module. However, as you can read in this issue on Github, Jake Wharton explains that this is going to take some time since Retrofit depends on other libraries such as OkHttp.

The best option we can find until now (March 2022) is to use Ktor for consuming REST APIs.

¿And what we should use as a local data source library? For that, there’s SqlDelight, and the implementation for a KMM project is pretty straightforward! We’ll need to create the database driver for each platform and put the data source implementations in the commonMain (core package inside the shared module). They (data sources) are going to depend on the database driver interface. Easy!

Presentation

This is the easy one (or maybe not). Here we only have to write the UI of each platform! That is, use Jetpack Compose for Android, SwiftUI for iOS, etc.

If you want to try something more sophisticated, you can try to add a presentation package inside the shared module and define the contracts for Views, Presenters and ViewModels (in case you want to implement the MVP or MVVM patterns). That is something that I have not tried yet, to be honest, but it could be very interesting.

Finally…

This is the final architecture design of our project:

As you can see, we are maximizing the approach of having a unique-cross-business layer that could be used by any platform.

And our project tree should look something like this:

As you may have noticed, we are starting to build the software from the core, from the business logic or domain layer. The rules and needs of the business will guide the development of the software: Domain Driven Development.

Note: I’m developing an open-source Kotlin Multiplatform project in Github, check it out!

Split of this post series

This has been the second post in which we have designed the solution for our project. In the following, we will talk about the domain layer implementation. This guide will be divided into the following posts:

  1. Introduction
  2. Designing the solution (this post)
  3. Creating the domain layer
  4. Creating the layer data
  5. Implementing the presentation layer
Jose Flavio
Jose Flavio

Written by Jose Flavio

Senior Software Engineer. Education, science and IT enthusiast. I have a blog! 😄 https://jflavio.com

Write a response