oop - 域对象可以与持久化对象相同吗?

标签 oop domain-driven-design cqrs

我们团队有一个基本上是 MIS 的项目。 我们使用的架构是 CQRS 和 DDD(领域驱动设计)。 我们有持久层中的持久对象、域层中的域对象、用于携带用户输入信息的数据传输对象以及用于在特定页面上显示数据的 View 对象。

我确实觉得这样的设计很棒。但在项目实现过程中,我们发现了一些问题,给我们带来了很大的阻碍。最恶心的是我们必须编写许多转换器来在两个不同层之间转换对象,例如 PO 到 DO、DO 到 PO、DTO 到 DO。这些转换器中有太多 get 和 set 语句。我们不使用 BeanUtils 这样的东西的原因是,当两个对象中的字段具有不同的类型或名称时,它不能很好地工作。显然这些代码违反了开闭原则。每当特定页面发生变化或者我们想要更改数据库中的某个字段时,这将是一场噩梦。

我想知道是否真的有必要将 DO 和 Po 分开,我们是否可以简化架构和设计以使它们变得相同,因为在大多数情况下它们只包含相同的字段,几乎没有什么区别。 如何简化设计,避免我们面临的问题,提高生产力,保证软件的可扩展性和稳定性?

最佳答案

如果您查看 DDD 战术模式,您将找不到任何有关“域对象”的内容。您有由实体组成的聚合,其中一个实体就是您的聚合根。每个有界上下文可能只有一个实体,这将是您的聚合根。

你将你的聚合作为一个整体来保存。基本存储库以“集合样式”运行,允许您仅将新聚合放入存储库并通过其标识从存储库检索一个聚合。

CQRS 将一个对象一分为二。一个对象是为写入而优化的聚合。通常,在“写入端”的命令处理程序中使用集合样式存储库就足够了。然而,在“阅读方面”,您拥有更扁平的阅读模型,它们代表您希望以针对阅读进行优化的方式向用户展示的内容。

如果你说你使用 CQRS 但只有一个模型和一个数据库,那么这没有多大意义。如果您有命令处理程序,并不意味着您使用 CQRS。

澄清之后,我们可以看看聚合持久性。 没有人说过你不能按原样持久保存聚合。在某种程度上,如果您有适当的存储,这实际上是首选方法。通常,ORM 不是一种合适的存储,因为它会在对象世界和关系数据库世界之间发现阻抗不匹配的地方产生摩擦。文档数据库更适合于此。 PostgreSQL JSONB 功能也是一个不错的选择。

流程将是:

  • DTO(命令)从客户端发送
  • 工作单元开始
  • 命令处理程序从存储库检索聚合
  • 命令处理程序调用聚合上的方法来执行必要的逻辑操作
  • 聚合可能会发出一些域事件,这些事件可以触发同一工作单元内的事件处理程序
  • 工作单元提交更改

正如您所看到的,这里没有任何持久性“转换”的真正位置。如果您有适当的存储空间,那就一切都很好。

但是,当您阅读时,您会收到来自客户端的请求。然后,该请求将发送到某个查询提供程序、数据提供程序或您所说的任何内容。有些人将所有这些查询放入其存储库中,但这仅在您使用 CQRS 或至少有一个持久性模型(而不是两个)时才有效。

查询是幂等的,它们不会改变系统的状态,并且可以根据需要运行任意多次,而不会产生任何后果,除非如果读取未优化,则可能会加载持久层。然而,没有必要在读取端放置大量抽象。同时,您不应将聚合发送回查询您的域的客户端。您需要有一个 DTO 来代表特定需求的客户端 View 模型,不能少也不能多。该 DTO 需要进行优化,以便尽可能快地在客户端呈现,无需计算和转换。这基本上就是 CQRS 建议在读取端执行的操作 - 准备数据以快速交付这些 DTO。

关于oop - 域对象可以与持久化对象相同吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39762354/

相关文章:

design-patterns - 领域驱动设计中依赖注入(inject)的最佳位置

php - OOP MySQLi 连接

domain-driven-design - DDD 和避免 CRUD

event-handling - 没有 CQRS 的领域事件和版本控制

c# - 如何配置 Autofac 以解析 CQRS 处理程序并在 Web API 项目中编写其查询调度程序

java - 将所有事件从 CommandGateway 路由到单个事件处理程序

asp.net - 图片上传 - 使用 CQRS 和 DDD 将代码放在哪里

java - 为交易系统设计订单对象

java - 玩家移动的 ActionListener 或 Action

C# 类变量的定义类型