how to avoid mappers

Clean Architecture: How to avoid mappers

Mohsen Beiranvand
3 min readOct 22, 2020

One of the problems in clean architecture is mappers. Because you have some entity in Domain module and on the other hand you need those entities in the Data module with extra annotation. For example, you need to persist that entity in the Room database so you should use some sort of annotations but Domain module doesn’t know anything about them. The Domain module just knows Java annotations.

It’s an error-prone way to map two entities because when your application goes to be larger, it’s too hard to keep tracking all entities when they are going to change and it’s possible that you change those entities and forgot to add it to the mapper and so on. Also for an entity, you should always change 3 different classes.

Let’s see an example of the process:

package com.imohsenb.clean.domain.data;public class Person {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

in the Data layer we have:

package com.imohsenb.clean.data.db;@Entity("Person")
public class PersonEntity {

@PrimaryKey
private Long id;
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

And a simple mapper:

public class PersonDataMapper implement DataMapper<Person, PersonEntity> {   @Override
public Person from(PersonEntity personEntity) {
Person person = new Person();
person.setName(personEntity.getName());
return person;
}
@Override
public PersonEntity to(Person person) {
PersonEntity personEntity = new PersonEntity();
personEntity.setName(person.getName());
return personEntity;
}
}

Consider that we would like to add another property to the Person class. We should change the Person, PersonEntity, and PersonDataMapper and you should be careful not to forget to change all of them (I know it’s hard).

Solution

Domain layer, World of Contracts!

In the Domain module (layer), we have too many interfaces to handle business logic and we do everything based on these contracts. Abstraction keeps our domain module clean and it does not depend on any implementation.

Let’s turn the Domain entities into contracts and called them ‘Model’.

Using interfaces instead of entities help us to handle this concern and makes changes easier and safer, and of course, it prevents to be depended on implementation!

Create an interface with some sort of getter and setter methods. Therefore just ask for what you need or what you want to set in domain logics:

package com.imohsenb.clean.domain.data;public interface PersonModel {

String getName();
}

As you can see in the code, in the model, we just ask for requirements and it doesn’t care how its requirement was satisfying.

But, what happens in the Data layer?
We need to implement the model interface in data classes:

package com.imohsenb.clean.data.db;@Entity("Person")
public class Person implements PersonModel {

@PrimaryKey
private Long id;
private String name;

@Override
public String getName() {
return name;
}
}

Hence, if you change ‘PersonModel’ through the Domain layer, you got a compile-time error to change the model’s implementation in the ‘Person’ data class.

What about the Presentation layer?

I would like to keep data classes for the Data layer. Because data classes are for satisfying Domain data requirements. So, I prefer to use Domain Models within the Presentation layer even in view layouts to reduce dependencies on implementation.

<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android" />
<data>
<variable
name="personModel"
type="com.imohsenb.clean.domain.data.PersonModel" />
</data>
...
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@{personModel.getName()}"
android:background="#CBFFFFFF"
android:textAlignment="center" />
...

Bear in mind, we make the logic based on Abstraction and satisfying them with implementations.

--

--

Mohsen Beiranvand
Mohsen Beiranvand

Written by Mohsen Beiranvand

Staff Android Engineer @ Truecaller

Responses (3)