Beh*_*zad 2 domain-driven-design software-design
当我在软件项目中实践 DDD 时,我总是面临这样的问题:“为什么我应该在实体中实现我的业务规则?它们不应该是纯数据模型吗?”
请注意,根据我对 DDD 的理解,领域模型可以由持久模型和值对象组成。
我提出了一个解决方案,将持久模型与域模型分开。另一方面,我们有数据传输对象 (DTO),因此我们有 3 层数据映射。数据库到持久性模型、持久性模型到域模型以及域模型到 DTO。在我看来,我的解决方案不是一个有效的方案,因为必须付出太多的努力。
那么有没有更好的做法来实现这个目标呢?
免责声明:这个答案比问题稍大一些,但需要理解问题;也是100%基于我的经验。
你的这种感觉很正常,我前段时间也有过同样的感觉。这是因为体系结构、编程语言和所使用的框架的组合。您应该尝试选择上述工具,以便它们提供最容易更改的代码。如果您必须为添加到实体的每个字段更改 3 个类,那么这在大型项目(即 50 多个实体类型)中将是一场噩梦。
问题是每个实体/概念有多个 DTO。
我使用的最重的架构是经典分层架构;严格版本是最难的(在严格版本中,一个层只能访问它之前的层;即用户界面只能访问应用程序)。当数据从基础设施转移到 UI 时,它涉及大量的 DTO 和转换。测试也很困难,因为我不得不使用大量的模拟。
然后我反转了依赖关系,域将不再依赖于基础设施。为此,我在域层中定义了在基础设施中实现的接口。但我仍然需要对他们使用嘲笑。此外,聚合并不纯粹,并且有副作用(因为它们称为基础设施,即使它是通过接口抽象的)。
然后我将域移到了最底部。这使我的聚合变得纯净。我不再需要使用嘲笑。但我仍然需要 DTO(由应用程序层返回给 UI 以及 ORM 使用的 DTO)。
然后我迈出了第一步:CQRS。这将模型分为两部分:写入模型和读取模型。重要的是您不再需要对模型使用 DTO。聚合(写入模型)可以按原样序列化,也可以转换为 JSON 并存储在几乎任何数据库中。Vaughn Vernon 有一篇关于此的博客文章。
但最好的是 Read 模型。您可以为每个用例创建一个读取模型。作为仅用于读取/查询的模型,它可以尽可能简单/转储。读取实体仅包含与查询相关的行为。只要有正确的坚持,它们就可以保持原样。例如,如果您使用MongoDB(或任何文档数据库),通过一个简单的基于反射的序列化器,您可以拥有一个非常薄的架构。由于域事件,您不需要使用 JOINS,您可以进行完整的数据非规范化(读取实体包括它们需要的所有数据)。
第二次飞跃是事件溯源。有了这个,您不需要聚合的平坦持久性。每次处理命令时,它们都会从事件存储中重新水化。
您仍然拥有 DTO(命令、事件、读取模型),但每个实体/概念只有一个 DTO。
关于消除演示文稿使用的 DTO:您可以使用GraphSQL之类的东西。
编程语言和框架可能会使上述所有情况变得更糟。强类型编程语言强制您为每个自定义返回值创建一个类型。某些框架强制您返回自定义可序列化类型,以便通过 HTTP 请求将它们返回到 REST(通过这种方式,您可以使用反射拥有自我描述的 REST 端点)。您PHP可以简单地使用带有字符串键的数组作为 REST 控制器返回的值。
聚苯乙烯