清洁架构中的实体应该知道持久性机制吗?

Posted

技术标签:

【中文标题】清洁架构中的实体应该知道持久性机制吗?【英文标题】:Should Entities in Clean Architecture know of persistence mechanisms? 【发布时间】:2018-11-29 07:25:09 【问题描述】:

在“清洁建筑”一书中 (Robert C. Martin) p。 191,他说“实体是纯粹的商业逻辑,没有别的”。关于持久性机制的实体知识,我不确定我应该如何解释这个陈述。

我假设实体对象是有状态的——它们操纵它们所代表的业务数据。如果是这样,则必须通知持久层对该数据的更改,以便它可以持久保存这些更改。所以;实体是否允许持有对持久性接口(或工作单元接口,如果设计更精细)的引用?

我倾向于认为持有这种引用(并从实体内部调用它)的实体对象将不是“纯业务规则”。但我有一种感觉,只要实体持有对接口的引用,它就不算数?

如果实体不应该持有对持久性机制的引用,是否还有其他好的模式可以持久化对业务数据的更改?

【问题讨论】:

【参考方案1】:

关于这个问题有两种主要的思路。它们都由不同的设计模式表示。这两个选项还考虑到您正在处理对业务场景的各个方面进行建模的有状态实体,从这个意义上说,它们知道将要持久化的“数据”,但是它们不一定知道持久性机制本身.

现在,关于持久性机制,第一种方法可能是旧 J2EE 或 Rails 从业者最熟悉的,其中实体完全知道它将被加载/保存到底层持久性中,并且它的接口将传达“get”、“insert”、“update”等方法。这被称为“Active Record”(Martin Fowler,企业应用架构模式)模式。也就是说,实体在对业务的某个方面进行建模时,它也将代表数据库中的直接记录,并且能够自行保存/加载。

另一种方法更符合您提到的“清洁架构”,一些作者将其称为“数据映射器”(也称为 Martin Fowler,企业应用程序架构模式)图案。在这件事上,实体仍然对持久性机制一无所知(它将是“纯业务逻辑,仅此而已”),并且您将“映射”实体的“数据”的责任委托给外部参与者(类/其他)目前持有和退出持久化机制/层。

换句话说,当采用这种方法时,您将把理解持久性机制以及从数据库到实体以及从实体到数据库进行翻译的责任委托给翻译人员。这样一来,您的实体甚至都不会意识到它们被持久化在其他地方,更不用说这种持久化过程的内部运作了。

persistence Data Mapper 的接口大致如下:

interface IMyDataMapper 
    void Save(IMyEntity entity);
    IMyEntity Get(whatever criteria you use to find the entity);

所以,从那个接口来看,它的职责很明确:

它接收一个实体(它不知道此操作)并读取其数据以将其存储在其他位置。 它接收在其他地方查找存储数据的条件,找到它并使用此数据填充实体对象以将其返回给您。

【讨论】:

谢谢!但是,数据映射器如何知道实体是否已更改?我最初提出问题的原因是我正在考虑实施呼叫者注册模式(来自您提到的 POEAA)。这要求实体有权访问工作单元引用。 是简单比较实体对象和数据库的常用方法吗? Data Mapper 模式可以不主动知道实体是否改变。如果需要,您可以设计具有明确命令和查询方法定义的实体(不要与完整的 CQRS 模型混淆),并让决定是否保存实体取决于您架构的应用程序层。这样,应用程序层就会了解是否在您的哪个实体上调用了 Command 方法(即状态更改方法),因此它可以决定需要保存哪些实体(无需了解模型遭受的实际更改是什么) . 如果您关心的是性能,并且您要避免覆盖从未更改的数据(而不是在命令时完全保存实体),您可以实现的是某种“上下文”,您可以在其中在加载数据时注册数据,并在应用层命令数据映射器持久保存时进行比较。因此,应用层命令数据映射器保存实体,映射器将新实体与“上下文”中存储的实体进行比较,并决定具体需要更新哪些部分数据。 我认为你的观点很好。我当前的实体设计(实际上是聚合设计参考域驱动设计)沿着查询(询问信息)和命令(执行状态更改操作)行分开 - 假设我对命令/查询方法的理解是正确的。所以现在,如果一个交互器调用一个实体命令方法,交互器本身应该知道实体改变了状态。【参考方案2】:

他说“实体是纯粹的业务逻辑,没有别的”。我是 不确定我应该如何尊重地解释这个陈述 让实体了解持久性机制。

非常真实。

业务对象不应该知道它们是如何被持久化的。

你的应用架构应该是:

业务层 - 包括实体、业务规则、领域事件、存储库接口等。 数据层 - 引用业务层并实现存储库接口。 服务层 - 引用业务层,协调应用程序的逻辑并使用来自业务层的存储库接口持久化数据。

【讨论】:

【参考方案3】:

这主要基于意见 - 您可能会发现有人投票结束此问题。

不过……

当我解释这句话(以及这本书本身,虽然我读它已经有一段时间了)时,目的不是定义一个可直接实施的技术架构,而是为您提供一种评估您的决策的方法。

在频谱的一端,您可能有一个将显示、应用程序、业务和持久性逻辑混合在一起的组件。我们都同意这不是“干净”的任何标准,更不用说鲍勃叔叔的了。

在光谱的另一端是一个纯粹的、干净的设计,其中业务实体没有参考持久性。例如,您可以通过发送消息/事件来实现这一点;业务实体决定一个业务流程已经完成,并发送一条消息来说明,然后持久性引擎决定何时以及如何将其写入存储。

根据我的经验,实际上,我们发现自己处于这两端之间的某个范围内,应该寻求“清洁”,而不是一次性实现“清洁”。

【讨论】:

嗨,Neviille Kuyt,你能回答我的问题吗?我对此感到非常沮丧。***.com/questions/50998636/…【参考方案4】:

我认为,即使您不使用 Clean Architecture,您的实体也应该对持久性机制一无所知,因为这些机制可能是可变的并且任意复杂。

您提出的问题的解决方案是让正在更改实体的层也确保这些更改保持不变。

【讨论】:

【参考方案5】:

实体 (DTO) 不应该知道持久性机制。 因为干净架构的想法是让你的整个业务逻辑独立于 UI 和框架。通过提供 DTO 内部持久性机制的知识,您的实体将变得依赖于框架。

交互应该是这样的:

UI VM/Presenter * 用例(交互器)* 框架上的适配器(插件)

实体应该在用例和插件之间使用。因此,如果驻留在具体用例实现中的业务逻辑操作实体,它可以直接调用插件的方法来保存更改,因为它持有对该插件的引用。

【讨论】:

以上是关于清洁架构中的实体应该知道持久性机制吗?的主要内容,如果未能解决你的问题,请参考以下文章

我应该忽略_SCoreData ConstraintViolationException 吗?

清洁架构、用例和实体

DDD 域实体与持久性实体

redis持久化机制

面试官:能说说Redis的持久化机制吗?

三层架构