Unlocking the Power of Mappers in Android: Simplifying Data Transformation in Clean Architecture

SUMIT KUMAR
6 min readJun 4, 2023

--

What are mappers and how do mappers facilitate data transformation between layers (Data and Domain) and contribute to achieving a CLEAN architecture in Android development?

In this article we will explore the significance of mappers in Android development and their integral role in implementing Clean Architecture. Learn how mappers facilitate data transformation between layers, enhance code maintainability, and improve testability. You will be having practical examples of mapper implementation and unlock the potential to streamline your Android app development with clean and efficient architecture.

CONTENTS

  • NEED OF MAPPERS(THEORY)
  • ABSTRACT
  • IMPLEMENTATION
  • THANKS AND REGARDS

What is the need of mappers?(you can jump to abstract if don’t want detail).

The need for mappers arises from the desire to achieve a CLEAN (CLear, ENcapsulated) architecture in Android development. Mappers play a crucial role in facilitating data transformation between layers, specifically between the Data layer and the Domain layer. Here are the key reasons why mappers are needed:

  • Separation of Concerns: Mappers separate the responsibility of data transformation from other components, ensuring a clear separation of concerns within the architecture. They handle the conversion of data objects between different layers without polluting the core logic of each layer.
  • Decoupling Layers: By using mappers, the Data layer and Domain layer can remain independent and decoupled. Each layer can define its own models and structures without being directly influenced by the data representation of the other layer.
  • Abstraction and Encapsulation: Mappers provide a layer of abstraction, encapsulating the data transformation logic within dedicated mapping classes. This allows for cleaner and more maintainable code, as the transformation details are hidden from other components.
  • Adaptation to Data Source Changes: Mappers allow for seamless adaptation to changes in the data source. If the structure or representation of data changes in the Data layer, the corresponding mapper can be modified accordingly, ensuring that the Domain layer remains unaffected.
  • Domain Model Consistency: Mappers ensure consistency in the Domain layer by mapping data from different sources to a unified/combined/centralised domain model. This ensures that the core business logic operates on a consistent set of data objects, regardless of the specific data sources and this is the most valuable benefit of mappers.
  • Testability: This is the real sweet we gonna achieve after doing all these shits:) →Mappers simplify the testing process by providing a clear separation of data transformation logic. Unit tests can be written specifically for mappers to verify that data is correctly transformed between layers.

Abstract

Mappers are essential in Android development to facilitate data transformation between the Data layer and the Domain layer, ensuring a CLEAN (CLear, ENcapsulated) architecture. They provide separation of concerns, decoupling of layers, abstraction and encapsulation of data transformation logic, adaptation to data source changes, consistency in the Domain model, and improved testability. Overall, mappers enable a more modular and maintainable codebase with clear boundaries between layers, enhancing the overall architecture of an Android application.

Implementation

Note:- I am assuming you are having basic knowledge of android development and familiar with CLEAN.
Here I am partitioning it feature wise.

Follow the steps-

1: Create new project-> Create new package with feature name-> as usual for CLEAN create mandatory 3 packages(data,domain,presentation), here I am also having common package for some common models and Mapper Interface.

2: In common package create a Mapper Interface.

interface Mapper<F,T> {

fun mapFrom(from:F):T

}

here “F” & “T” is the type of class(data class most of the time) to be converted and into converted respectively.{don’t worry you will get it after few steps}.

3. Create mapper package in Domain layer where all the mapper classes will be declared.

I hope you are familiar with rest of the packages.

4. Now park the mapper thing here and, lets take a data class in your data layer, and it is a response coming from remote server , so you have created the related data classes in model package of data layer. Named as( SumitDto and EducationDto).

data class SumitDto(
@SerializedName("name_param")
val nameParam: String,
@SerializedName("height_param")
val heightParam: Int,
@SerializedName("education_param")
val educationParam: EducationDto
)
data class EducationDto(
@SerializedName("course_param")
val courseParam: String,
@SerializedName("college_param")
val collegeParam: String
)

5. And so you need to create Models in domain layer according to the need of data in presentation layer, here in my case I need all the the params , thats why I need to create identical models in domain layer.

data class SumitModel(
val name: String,
val height: Int,
val education: EducationModel
)
data class EducationModel(
val course: String,
val college: String
)

6. Now its time to pick mapper again → implementation of mapper. Our purpose is to fetch the response from server in DTOs(in data layer) and convert it to MODELs(in domain layer). → create a kotlin file in mapper package under domain layer(here we will define logic of mapper)

7. Define a class and extend it with Mapper interface( we created in 2nd step). Here we want to convert SumitDto to SumitModel, so “F”= SumitDto and “T”=SumitModel.

Note- I am using hilt for DI, you can ignore @Inject constructor().

here we will also need mapper for EducationDto to convert it into EducationModel and so implementation for it is also there.

//mapper for SumitDto
class SumitMapperDtoToModel @Inject constructor() :
Mapper<SumitDto?, SumitModel?> {
override fun mapFrom(from: SumitDto?): SumitModel {
return SumitModel(
name = from?.nameParam ?: "",
height = from?.heightParam ?: 0,
education = from?.educationParam?.let { EducationMapperDtoToModel().mapFrom(it) } ?: EducationModel(
course = "",
college = ""
)
)
}
}

//mapper for EducationDto
class EducationMapperDtoToModel @Inject constructor() :
Mapper<EducationDto?, EducationModel?> {
override fun mapFrom(from: EducationDto?): EducationModel {
return EducationModel(
course = from?.courseParam ?: "",
college = from?.collegeParam ?: ""
)
}
}

8. Utilization of mapper is too simple , as you can see in SumitMapperDtoToModel also how EducationMapperDtoToModel mapper is used for transformation.

EducationMapperDtoToModel().mapFrom(it)

The same way one can use SumitMapperDtoToModel too.

SumitMapperDtoToModel().mapFrom(it) // where it wll be SumitDto.

Thank you for reading this article on the importance of mappers in Android development and their role in achieving a CLEAN architecture. I hope the information provided has expanded your understanding and knowledge of this crucial concept. If you have any further questions or would like to contribute to the discussion, please feel free to leave your comments below. Your feedback and engagement are greatly appreciated. Thank you once again, and happy coding!

INIT KOTLIN : )

--

--

SUMIT KUMAR

SDE(Android) @Raaho || Ex-Ingenium || Modern Mobile Developer📱