干净的架构:在不同的层共享相同的模型/实体

dev*_*vha 5 architecture android kotlin clean-architecture

在我干净的架构 Android 应用程序设置中,我为每一层(数据、域、表示)都有自己的 Gradle 模块。我也为每一层都有自己的模型/实体,它们使用映射器从一层转换到另一层。这导致我有很多 kotlin 数据类,代表基本相同的东西,但在不同的层。这对我来说听起来不对。

简单的例子:

数据层- Android 库模块

@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
)
Run Code Online (Sandbox Code Playgroud)

领域层- 纯 Kotlin 模块

data class Building(

    val id: String,

    val name: String,

    val location: CoordinatePoint,

    val tenants: List<Tenant>? = null
Run Code Online (Sandbox Code Playgroud)

表示层Android 应用模块

data class BuildingModel(

    val id: String,

    val name: String,

    val location: LatLng,

    val tenants: List<TenantModel> = listOf()
)
Run Code Online (Sandbox Code Playgroud)

BuildingEntity 从外部网络api获取。

这很好地将每个模块彼此分开,但在我的应用程序中,我有很多具有嵌套结构的不同实体。所以我最终写了很多 kotlin 数据类和映射器。

我怎样才能简化这个?我可以删除Building类并BuildingEntity在数据和域层上使用吗?只是转换BuildingEntityBuildingModel表示层?

我试图找到实用的答案,人们是如何解决这类问题的,而不是写了大量的数据类和映射器?

Len*_*nin 10

事实上,你这样做的方式是正确的。为了使应用程序完全干净,您应该在每个层中拥有不同的实体表示形式,并使用映射器转换它们。这允许您仅在需要更改某些实体时更改映射器。

例如,您可能从服务器收到一些您不希望在 UI 中显示的数据,因此您的演示实体中没有它。另一个例子是,如果您想更改数据实体中的参数名称。如果您直接从演示文稿访问它,则需要更改所有访问权限。相反,如果您有映射器,您唯一要做的就是更改映射器。

显然,清洁架构是一种复杂的架构,在大型且长期的项目中更有意义,因为这些项目可能会更频繁地发生变化。因此,如果您正在开发一个小应用程序,并且想摆脱大量代码,那也没关系。在这种情况下,我会建议您对域和表示使用相同的实体,并为数据保留映射器,因为由于数据依赖于 API,因此更改不取决于您,并且您将从映射器中获得一些好处。

  • 在项目所需的内容和做某事的“推荐方式”之间找到平衡本身就是一门艺术。但这是发展的一个重要方面。它正在寻找快速和正确执行之间的平衡。 (3认同)

Fre*_*ros 7

在我的域模块中,我有我的模型作为接口(Kotlin 允许我们在接口内有 vals),数据模块中的实现和演示中根本没有模型。

看看这个小样本:

领域:

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

interface UserRepository {
    fun getUserDetails(id: String): IUserModel
}
Run Code Online (Sandbox Code Playgroud)

数据:

@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
    }
}
Run Code Online (Sandbox Code Playgroud)

介绍:

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

没有映射器,没有大量的数据类。

我有几个使用这种结构的项目,有时需要一个映射器(将网络模型转换为数据库实体),但使用接口作为域中的模型可以大大减少冗长。

  • 好吧,你总是可以使用扩展函数来实现这一点。在您的演示文稿中: fun Building.getPositionLatLon() = LatLon(location.lat,location.lon) 这只是一个建议,如果变得复杂,您始终可以创建一个映射器;) (2认同)
  • 现在,当我使用这种模式取得了一些进展时,我在通过意图将模型传递给另一个活动时遇到了问题。所以我的模型需要实现 Parcable,但是由于模型(接口)是在数据层(纯 kotlin 模块)中定义的,所以我不能在那里使用 Parcable。也许我只是在数据层模型中实现 Parcable,并将表示层中的接口转换为 Parcable。但这听起来有点老套。 (2认同)