Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clean architecture: share same models/entities with different layers

In my clean architecture Android app setup, I have own Gradle module for each layer (data, domain, presentation). I also have own models/entities for each layers, which are converted from one layer to another using mappers. This leads to situation where I have a lot of kotlin data classes, representing basically same thing, but in different layer. This does not sound right to me.

Simple example:

Data layer - Android library module

@JsonClass(generateAdapter = true)
data class BuildingEntity(
    @Json(name = "u_id")
    val id: String,

    val name: String,

    val latitude: Double,

    val longitude: Double,

    @Json(name = "current_tenants")
    val tenants: List<TenantEntity>? = null
)

Domain layer - Pure Kotlin module

data class Building(

    val id: String,

    val name: String,

    val location: CoordinatePoint,

    val tenants: List<Tenant>? = null

Presentation layer Android app module

data class BuildingModel(

    val id: String,

    val name: String,

    val location: LatLng,

    val tenants: List<TenantModel> = listOf()
)

BuildingEntity is fetched from external network api.

This nicely separates each modules from each other, but in my app I have a lot of different entities with nested structures. So I end up writing a lot of kotlin data classes and mappers.

How I can simplify this? Can I remove Building class and use BuildingEntity on data and domain layer? Just convert BuildingEntity to BuildingModel on presentation layer?

Im trying find practical answers, how people are solving this kind of problem, not ending up to writing tons of data classes and mappers?

like image 544
devha Avatar asked Nov 27 '18 21:11

devha


2 Answers

Actually, it's right the way you are doing it. To make an app totally Clean you should have different representations of you entities in each layer, and convert them with mappers. This allow you to only change the mappers when you need to change some entity.

For example you may receive some data from the server that you don't want to be shown in UI, so you don't have it in your presentation entity. Another example would be if you want to change an argument name in the data entity. If you access it directly from presentation, you will need to change all the accesses. Instead, if you have mappers, the only thing you have to do is to change the mapper.

Obviously, Clean Architecture is a complex one, that get more sense in big and long time projects, where changes may arise more often. So, it's okay if you want to get rid of doing lot of code if you are doing a little app. In this case i will advice you to use the same entity for Domain and Presentation, and keep mappers for data ones, because as data depends on API, that changes doesn't depend on you, and you would get some benefits from mappers.

like image 39
Lenin Avatar answered Oct 17 '22 04:10

Lenin


In my domain module I have my models as interfaces (Kotlin allow us to have vals inside interfaces), the implemenations in data module and no models in presentation at all.

Take a look at this small sample:

domain:

interface IUserModel {
    val id: String
    val name: String
}

interface UserRepository {
    fun getUserDetails(id: String): IUserModel
}

data:

@Entity
data class UserEntity(
    @SerializedName("userId")
    override val id: String,
    override val name: String
) : IUserModel

class UserRepositoryImpl(val userDao: UserDao) : UserRepository {

    override fun getUserDetails(id: String): IUserModel {
        return userDao.getUser(id) //userDao.getUser returns a UserEntity
    }
}

presentation:

class UserDetailsViewModel(val userId: String, val userRepository: UserRepository) : ViewModel() {
    val userData: LiveData<IUserModel> = MutableLiveData()
    fun getUserData() {
        (userData as MutableLiveData).postValue(userRepository.getUserDetails(userId))
    }
}

No mappers, no tons of data classes.

I have a couple of projects with this sctructure and sometimes a mapper is needed (convert network models to database entities) but the verbosity is widely reduced using interfaces as models in domain.

like image 70
Fredy Mederos Avatar answered Oct 17 '22 04:10

Fredy Mederos