清洁架构:为不同的数据源使用不同的模型类?

Posted

技术标签:

【中文标题】清洁架构:为不同的数据源使用不同的模型类?【英文标题】:Clean Architecture: Use different model classes for different data sources? 【发布时间】:2018-02-02 09:27:53 【问题描述】:

我目前正在开发一个新闻提要安卓应用。我尝试根据干净架构的原则设计我的应用程序。

在数据层中,我使用存储库模式作为不同数据源的外观:来自 API (https://newsapi.org/) 的远程数据、来自数据库(Realm 或 SQLite)的本地数据以及一些内存中的数据缓存。 在我的领域层中,我定义了一些不可变的模型类(Article、NewsSource 等),它们正在被领域层和表示层使用(我认为表示层中不需要额外的模型类)。

对远程数据源和本地数据源使用不同的模型类是否有意义?

例如远程数据源使用 Retrofit 进行 API 调用,模型需要注解才能被 GSON 解析。

data class RemoteArticleModel(
        @SerializedName("title") val title: String,
        @SerializedName("urlToImage") val urlToImage: String,
        @SerializedName("url") val url: String)

本地数据源的模型也可能必须履行某些特定合同,例如 Realm DB 中的模型需要扩展 RealmObject。

open class Dog : RealmObject() 
    var name: String? = null
    @LinkingObjects("dog")
    val owners: RealmResults<Person>? = null

显然,我不希望我的域模型被任何特定于数据源的合同(注释、RealmObject 继承等)“污染”。所以我认为对不同的数据源使用不同的模型是有意义的,并且存储库会处理它们之间的映射。

例如我们想从远程 API 中获取所有文章,将它们存储在本地 DB 中并返回给领域层。

流程如下: 远程数据源向新闻 api 发出 http 请求并检索RemoteArticleModel´s 的列表。存储库会将这些模型映射到特定领域的文章模型 (Article)。然后这些将被映射到数据库模型(例如RealmArticleModel)并插入到数据库中。最后Article的列表将返回给调用者。

出现两个问题: 上面的示例显示了使用这种方法会有多少分配。 对于要下载并插入数据库的每篇文章,将在该过程中创建三个模型。会不会有点过分了?

另外,我知道数据层应该使用与领域层不同的模型类(内层不应该与外层无关)。但是在上面的例子中这有什么意义。对于两个不同的数据源,我已经有了两个不同的模型类。添加第三个被数据层/存储库用作“中介”模型以处理到其他模型(远程、本地、域)的映射将增加更多的分配。

那么数据层是否应该对领域模型一无所知,让领域做从数据层模型到领域层模型的映射?

是否应该有一个仅由存储库/数据层使用的通用模型?

谢谢,非常感谢经验丰富的开发人员提供的任何帮助 :)

【问题讨论】:

这是你在说的吗? github.com/sahilNaran/layeredMvp? 是的,类似的东西,非常感谢。此示例将域模型暴露给数据层。那么这是一个有效的做法吗?还有为什么这个项目对数据、域等使用不同的模块?这是为了进一步解耦层吗? 有效,因为数据层没有外泄。是的,如果您查看 gradle 文件,可以进一步解耦,但是没有必要走那么极端。它还有助于确定范围,这样我就不会意外使用错误的类(我知道可以使用命名空间来完成),但确实会阻止访问 泄露出去是什么意思?另外,这些分配在性能和垃圾收集方面可以忽略吗? 谢谢,这让事情变得清楚了。我想使用不同模型给我的灵活性弥补了分配。毕竟,如果会对性能产生更大的影响,我可以重新考虑我的策略,但现在我会采用你建议的方法,我已经部分使用了。所以非常感谢:) 【参考方案1】:

您应该遵循的首要原则是关注点分离。

持久层应该有只处理数据存储和检索的类,在本例中是 Realm 类。

网络层应该有处理来自服务器的数据的类,在本例中是 Retrofit 类。

将数据从这些层中的任何层移动到您的业务层需要您将持久性和网络对象映射到您的域。

为了回答您的第一个问题,映射隔离了不同的关注点,将域与数据层分开。数据层不应该知道领域模型。领域向数据层请求数据,数据层获取数据并通过映射器传递,从而返回领域模型。

要回答您的第二个问题,如果您从不同来源获取数据,则为数据层建立通用模型将违反关注点分离。持久性模型和网络模型代表系统的不同部分,因此应该用不同的模型来表示。域不需要知道这一点,因此请求的数据应在越界返回域之前映射到域对象。

【讨论】:

您的解释完全有道理,谢谢。不过我有一个问题: 数据层不应该知道领域模型。域从数据层请求数据,数据层获取数据并将其通过映射器传递,从而返回域模型。 所以我将为每个数据源特定模型(数据库、远程等)创建一个映射器.) 哪个转换为域模型对吗?存储库门面应该负责映射这些模型吗?还是应该由单个数据源负责直接映射模型,而存储库只能使用域模型? 最简洁的方法是为每个要通过边界传输的对象设置一个单独的映射器。这样您就实现了单一职责,即映射器将负责将数据对象转换为域对象。而数据类只关心它所代表的数据 @Brian 在跨越层边界之前你有这个映射的例子吗?【参考方案2】:

添加到@Brian 答案,可能您可以添加或封装数据层,如Clean Boilerplate 建议:

这样你就有了一个通用的数据模型,它被映射到领域模型。我不确定这是否会添加不必要的代码和层,因为这样数据和域模型可能看起来几乎相同。

【讨论】:

以上是关于清洁架构:为不同的数据源使用不同的模型类?的主要内容,如果未能解决你的问题,请参考以下文章

三层架构

DDD层和清洁架构

网络分层架构

清洁代码,清洁架构和清洁项目布局/项目结构

Django为同一模型创建不同的表

清洁架构与洋葱架构