乐于分享
好东西不私藏

领域驱动设计-软件核心复杂性应对之道(7)_领域对象的生命周期

领域驱动设计-软件核心复杂性应对之道(7)_领域对象的生命周期
本章的核心观点是: 管理对象从创建到持久化再到检索的整个生命周期,同时保持模型的完整性。主要挑战是:(1) 在整个生命周期中维护对象的完整性(固定规则);(2) 防止模型陷入管理生命周期复杂性的困境中。本章通过聚合、工厂和存储库三种模式来解决这些问题。

核心梳理

1. 模式:AGGREGATE(聚合)

  • 定义:一组相关对象的集合,被当作数据修改的单元。它定义了一个边界(包含哪些对象)和一个根(一个特定的实体,所有外部访问都必须通过根)。
  • 核心作用:维护固定规则。在每次事务完成后,聚合内部的所有对象作为一个整体必须满足这些规则。例如,"Purchase Order"及其"Line Item"对象组成一个聚合,提交订单时,所有"Line Item"的总金额必须与订单总额一致。
* 设计规则:
  1. 根实体具有全局标识。
  2. 边界内的实体具有本地标识(仅在聚合内部唯一)。
  3. 外部对象只能通过根来引用内部对象。
  4. 只有根才能被数据库直接查询,其他对象需要通过遍历关联来发现。
  5. 删除聚合时,必须删除其边界内的所有对象。
  6. 每次事务提交时,整个聚合的所有固定规则都必须被满足。

2. 模式:FACTORY(工厂)

定义:一个程序元素,负责封装创建复杂对象或整个聚合的知识。
核心作用:封装创建逻辑,使客户端无需知道对象的内部结构和具体类,从而解除客户端与具体实现的耦合。同时,确保创建出的对象满足所有固定规则。
适用场景:当创建过程复杂、涉及多个对象(如聚合)或需要隐藏具体实现时,使用工厂。简单的、没有多态性的对象可以直接用构造函数创建。
与聚合的关系:工厂通常用于创建整个聚合,并返回根的引用。

3. 模式:REPOSITORY(存储库)

  • 定义:一个对象,为需要全局访问的根实体(或聚合)提供检索和持久化的接口,其行为就像一个内存中的集合。
  • 核心作用:封装持久化机制。客户端可以使用简单的查询方法(如 "findById()" )来获取对象,而无需知道底层是关系数据库、文件系统还是其他存储方式。它解除了客户端与基础设施的耦合。
* 设计规则:
* 只为需要全局访问的聚合根提供存储库。对于聚合内部对象,通过根来访问。
* 存储库负责添加和删除对象,但事务控制权留给客户端。
* 存储库可以委托工厂来重建已存储的对象(从数据库读取数据后,使用工厂实例化)。

项目使用时的指导原则

基于以上核心思想,在项目中应用本章模式时,可以遵循以下原则:

1. 将“聚合”作为事务一致性的边界

* 原则:不要无脑地把所有关联对象都塞进一个大聚合。要基于业务规则和变更频率,设计小的、内聚的聚合。聚合内部保证强一致性,聚合之间保证最终一致性。
* 行动:
* 分步识别:1. 确定根实体;2. 识别哪些关联对象需要和根一起修改(强相关);3. 将临时引用排除在外。
* 通过标识引用其他聚合:在聚合内部,如果要引用另一个聚合,只保存其ID,而不是整个对象。这可以大大减少耦合和并发冲突。
* 警惕过度设计:固定规则不是越多越好。只强制那些“如果一次违反,就会导致业务崩溃”的规则。对于松散的约束,可以通过领域事件或最终一致性来处理。

2. 使用工厂封装复杂的创建逻辑

* 原则:不要让客户端或根实体自己承担创建聚合的复杂职责。将创建逻辑委托给工厂,使客户端专注于业务逻辑。
* 行动:
* 工厂方法:可以在根实体上定义一个工厂方法(如  "Order.createNewOrder (customerId, items)" ),但只适用于简单情况。对于复杂创建,使用独立的工厂对象("OrderFactory")。
* 分离创建与业务逻辑:工厂的职责仅仅是“创建”。不要在工厂中执行复杂的业务规则计算,这些计算应该由领域对象自身完成。
* 使用测试驱动开发:当工厂的创建逻辑变复杂时,就是开始编写单元测试的好时机,确保工厂返回的对象符合预期。

3. 存储库只用于聚合根,并解耦技术细节

* 原则:存储库是领域层的设计模式,而不是基础设施层。它通过定义接口来解耦。客户端只看到领域模型中的查询方法,而不见背后的SQL或ORM代码。
* 行动:
* 接口定义在领域层:在领域层中定义一个"Repository"接口(如 "OrderRepository" ),包含 "findById()"、"save()" 等方法。由基础设施层(如一个实现了该接口的 "JdbcOrderRepository" 类)去实现具体的数据库操作。
* 只适用于根实体:不要为聚合内部的实体(如 "Line Item")创建存储库,它们只能通过聚合根来遍历访问。
* 测试模拟:利用接口,在单元测试中可以轻松使用一个模拟存储库(如 "InMemoryOrderRepository"),而无需真正连接数据库,这大大提高了测试速度和隔离性。
总结: 在项目中运用“领域对象的生命周期”理念,核心是 “分而治之” 。其指导原则是:以聚合作为强一致性的边界,将创建复杂性的职责交给工厂,并用存储库来封装持久化技术,从而保护领域模型的纯洁性和可测试性。