【问题标题】:Right place to map to Domain in Android clean architecture在 Android 干净架构中映射到域的正确位置
【发布时间】:2021-12-10 22:34:53
【问题描述】:

我和我的同事正在争论将我们的实体对象或远程 dto 对象映射到简单的域对象的正确位置。

我们的结构是这样的。

源(包括 dao)> repo(包括源)> 用例(包括 repo)

我的同事认为映射到域应该在源内部完成,以便域对象可以传递到下一层

class SomeSourceImpl(private val dao: Dao) : SomeSource {
    override fun get(): Observable<DomainModel> {
        return dao.getResponse().map { it.mapToDomain() }
    }
}

我的同事认为,根据Uncle Bob,这是由于依赖规则。

这条规则说源代码依赖只能指向内部。 内圈中的任何人都无法知道关于某事的任何事情 一个外圈。特别是,在 内圈中的代码不得提及外圈。 这包括函数、类。变量或任何其他命名的 软件实体。

我非常不同意直接在源内部映射到域的方法,因为这样存储库就会变得贫乏,因此我们采用了贫乏存储库无用的反模式,他们所做的只是盲目地传播来自来源。 (现在您可能会说来源也很贫乏,我们可以简单地删除它们并将 dao 对象直接包含到 repo 中,但在我们的案例中这是不可能的)。

相反,我建议源将返回原始数据库实体(或远程实体,如果我们正在进行休息调用),因为源返回原始数据以供以后处理是有意义的。 repo 的工作是从源获取结果,然后将其映射到域,最后将此域对象传播到类似的用例。

class SomeRepoImpl(private val someSource: SomeSource) : SomeRepo {
    override fun get(haId: String): Observable<DomainModel> {
        return otherAssetSource.get().map { it.mapToDomain() }
    }

我还在 github 上遇到了一些示例,它们映射到其 repos 中的域而不是源

Here

Here

Here

Here 也是适用于 iOS 的一种

关于可以将实体映射到域对象的位置,干净架构原则中的严格规则是什么?

【问题讨论】:

  • 低层不能依赖高层的代码,所以映射到域层中的一个域。 “内圈中的任何人都无法对外圈中的事物一无所知。” source 是内圈,domain 是外圈。

标签: android kotlin clean-architecture


【解决方案1】:

引用规则

源码依赖只能指向内

这取决于我猜的架构。让我用一个例子来解释一下:

架构:

DOMAIN <- DATA <- PRESENTATION

地点:

DATA -> LOCAL  
|  
v  
REMOTE

注意: DOMAIN 代表最内圈,PRESENTATION 代表最外圈。

现在 DOMAIN 是一个纯 Kotlin 模块,没有任何 Android 依赖项。让我们定义一个存储库:

interface ProfileRespository {
    
    fun getProfile(): Profile?

    fun updateProfile(profile: Profile)
}

我们在 DATA 层(这是一个 Android 库)中实现这一点:

class ProfileRepositoryImpl(
    private val networkManager: NetworkManager,
    private val remoteDataSource: ProfileRemoteDataSource,
    private val localDataSource: ProfileLocalDataSource
): ProfileRepository {
     
    override fun getProfile(): Profile? {
        if(networkManager.isNetworkAvailable) {
            localDataSource.insert(remoteDataSource.get())
        }
        return localDataSource.get()
    }

    override fun updateProfile(profile: Profile) {
        remoteDataSource.update(profile)
    }
}
class ProfileRemoteDataSource(
    private val api: ProfileApi,
    private val mapper: Mapper<ProfileDto, Profile>
) {
   
    fun get(): Profile {
        return mapper.toModel(api.getProfile())
    }

    fun update(profile: Profile) {
        api.updateProfile(
            mapper.fromModel(profile)
        )
    }
}
class ProfileLocalDataSource(
    private val dao: ProfileDao
    private val mapper: Mapper<ProfileEntity, Profile>
) {
   
    fun insert(profile: Profile) {
        return dao.update(mapper.fromModel(profile))
    }
   
    fun get(): Profile? {
        return dao.getProfile()?.let(mapper::toModel)
    }
}

interface Mapper<T : Any, R : Any> {
    fun toModel(value: T): R
    fun fromModel(value: R): T
}

LOCAL 模块是一个独立于任何依赖项的 Android 库,并公开了 DAO 和 Entity 对象:

interface ProfileDao {
    fun insert(profile: ProfileEntity) 
    fun get(): ProfileEntity?
}

同样,对于REMOTE 模块:

interface ProfileApi {
    fun get(): ProfileDto
    fun update(profile: ProfileDto) 
}

所以,让Source 类返回DTO 和Entity 对象对我来说没有意义。 repo 类看起来像这样:

class ProfileRepositoryImpl(
    private val networkManager: NetworkManager,
    private val remoteDataSource: ProfileRemoteDataSource,
    private val remoteDataMapper: Mapper<ProfileEntity, Profile>,
    private val localDataSource: ProfileLocalDataSource,
    private val localDataMapper: Mapper<ProfileDto, Profile>
): ProfileRepository {
     
    override fun getProfile(): Profile? {
        if(networkManager.isNetworkAvailable) {
            val profile = remoteDataMapper.ToModel(remoteDataSource.get())
            localDataSource.insert(localDataMapper.fromModel(profile))
        }
        return localDataMapper.toModel(localSource.get())
    }

    override fun updateProfile(profile: Profile) {
        remoteDataSource.update(remoteDataMapper.fromModel(profile))
    }
}

在您的示例中,您只考虑了GET 操作。在这里,对于UPDATE 操作,我们还需要映射 DOMAIN 对象。因此,当我们添加更多功能时,如果对象的映射是在 Repo 类中完成的,那么 Repo 类会变得非常混乱。

我相信这将取决于系统的整体架构。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-06
    • 2016-06-24
    • 1970-01-01
    • 1970-01-01
    • 2020-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多