用事件替代你的DTO数据结构 - DZone


文中的想法最适用于实现(复杂)业务规则、状态转换并将其数据保存到某个数据库的后端应用程序。
复杂的逻辑应该在您可以完全控制内部域模型的数据结构上实现,您可以根据问题对其进行定制以简化代码。
这是本文中使用的术语定义的(自以为是的)列表:

  • 领域= 要保留应用程序逻辑中最复杂部分的代码区域。任何架构的目标都是清理实现系统基本复杂性的环境。
  • 数据传输对象 (DTO)  = 跨系统边界移动的对象,例如,编组为 JSON、XML 或二进制格式。
  • API = 系统的入口点(REST 端点或消息队列)以及所涉及的 DTO。许多系统都暴露了一个 REST API,然后依次调用其他系统的 API。
  • 领域对象 (例如,值对象、实体、聚合)= 应用程序的内部数据结构,通常保存在某些数据库中,但在系统外部不可见

 
避免 DTO 的原因
  • DTO 臃肿

由 第三方方 API 公开的大多数 DTO 更通用,并且包含的​​字段比您在应用程序中需要的属性子集更多。理解一种称为采用包含 3 个字段的对象的方法比采用具有 12 个字段(加上 2 个子列表)的参数的方法要容易得多。
  • DTO 扁平

设计 API 的一个关键问题是向后兼容性,也就是说,当 API 明天发生变化时,不会破坏现有的客户端。
很多时候 DTO 是扁平的,很容易退化为具有几十个属性的结构。另一方面,我们更喜欢在我们的领域中使用更小的结构,只有当我们更积极地将我们发现为嵌套对象的概念建模时,我们才能获得这种结构。
  • DTO 属于不同的有界上下文

领域驱动设计强调,当词语对不同的各方有不同的含义时,这些各方可能属于不同的有界上下文。
  • DTO 贫血

目前创建 DTO 的方法主要有 3 种:
  1. 手动写入
  2. 从“客户端库”导入
  3. 从 OpenAPI/swagger/WSDL 规范生成

选项 (3) 使得将自定义代码添加到 DTO 变得非常困难,而 (2) 坦率地说,这是不可能的。但是,如果您允许这些对象进入核心逻辑,您必须将行为推送到导致可怕Util或Helper类的其他地方,或者在您的服务中积累大量程序代码。
  • DTO 是可变的

许多(较旧的)系统仍然在其 DTO 上同时具有 getter 和 setter,特别是如果 DTO 捆绑在“客户端库”中或如果它们是生成的。
  • DTO 不受约束

数据传输对象传输数据。很神奇吧?他们的责任不是验证数据或保持一致。
然而,在我们公开的 API 中,许多 Java 团队选择向我们的“请求”DTO添加验证注释(例如@NotNull,、、@Size... @Pattern) 。之后,我们只要求框架检查所有内容。这很酷。  
然而,第 3 方 API 的 DTO从不带有约束。
因此,任何类型的 DTO(或)在 领域中使用时都可能呈现损坏的数据。
但是这里的“有效”意味着两件事:
  1. 简单的全局约束,例如“必填字段”。
  2. 强制执行我的域的一致性规则,例如“激活的合同设置了激活日期”。

如果您在领域驱动设计的上下文中探索第二条规则,您会发现聚合和领域事件。
  • DTO 不受您的控制

它们可能会因外部原因而改变:您被迫升级到 V2。如果您在这些结构上编写了数千行逻辑,那么祝您在新结构上重写所有逻辑会有好运。
 
banq:用领域事件替代或包装你的DTO:事件驱动系统中不同类型的事件

图中Policy代表业务规则,Command和Event属于DTO的升级版本,命令事件之间有因果关系(蓝色)和相关性关系(绿色),这两种逻辑关系见:简单说说康德的四组范畴