2. 领域驱动设计核心:实体、值对象、聚合、领域事件、限界上下文的概念与关系
领域驱动设计(DDD)提供了一套用于构建复杂业务软件的战术建模工具。理解其核心构造块——实体、值对象、聚合、领域事件和限界上下文——是进行有效领域建模的基础。这些概念并非孤立存在,它们共同构成了一个从微观到宏观、从静态到动态的完整建模体系。
2.1 核心构造块概览
下表从定义、标识、可变性与生命周期四个维度,对五个核心概念进行对比,帮助建立初步认知。
| 概念 | 核心定义 | 唯一标识 | 可变性 | 生命周期 |
|---|---|---|---|---|
| 实体 (Entity) | 具有唯一标识且会随时间变化的对象 | 必须有,贯穿始终 | 可变 | 由仓储管理,可持久化 |
| 值对象 (Value Object) | 描述某种属性或状态,无概念标识 | 无,由属性值定义 | 不可变 | 随所属实体创建/销毁 |
| 聚合 (Aggregate) | 一组相关对象的集群,保证数据一致性 | 由聚合根提供 | 通过聚合根操作 | 作为整体持久化 |
| 领域事件 (Domain Event) | 领域中发生的、业务人员关心的重要事件 | 有,通常包含时间戳 | 不可变 | 发布后即完成 |
| 限界上下文 (Bounded Context) | 模型应用的显式边界,包含语义和语言 | 无,是边界定义 | 边界内稳定 | 与子域或系统对齐 |
2.2 实体与值对象:领域模型的原子单元
实体和值对象是领域模型中最基本的两种对象类型,它们的区别直接决定了模型的设计质量。
2.2.1 实体:身份的守护者
实体是具有唯一标识的对象,其属性可以变化,但身份贯穿整个生命周期。例如,一个“订单”实体,即使其状态从“待支付”变为“已支付”,它仍然是同一个订单。实体的相等性由标识决定,而非属性。
2.2.2 值对象:属性的容器
值对象描述领域的某个方面,没有概念上的标识。它由一组属性值定义,且通常是不可变的。例如,“地址”是一个典型的值对象,由“省/市/区/街道”组成。如果地址变了,我们不会认为原来的地址“变了”,而是创建了一个新的地址对象。值对象的相等性由所有属性值决定。
2.3 聚合:数据一致性的堡垒
聚合是领域模型中用于保证数据一致性的核心模式。它将一组关联的实体和值对象封装为一个整体,并指定其中一个实体作为聚合根。外部对象只能通过聚合根来访问聚合内的任何对象,从而确保聚合内部的不变量在任何操作后都得到满足。
聚合的设计原则:
- 聚合根是唯一入口: 外部对象只能持有对聚合根的引用。
- 内部对象只引用聚合根: 聚合内的实体可以持有彼此引用,但不能被外部直接引用。
- 事务边界: 一个事务通常只修改一个聚合实例。
- 通过标识引用: 聚合之间通过聚合根的唯一标识(ID)进行引用,而非直接持有对象引用,这有助于实现松耦合。
2.4 领域事件:模型中的时间戳
领域事件是领域中发生的、业务人员感兴趣的过往事实。它记录了状态的变化,是连接聚合、实现事件驱动架构和最终一致性的关键。例如,“订单已支付”、“库存已耗尽”、“用户已注册”都是典型的领域事件。
领域事件的核心价值:
- 解耦: 发布者无需知道谁在监听事件。
- 审计与溯源: 事件日志提供了系统状态的完整历史。
- 最终一致性: 跨聚合或跨限界上下文的操作,可以通过事件实现最终一致性,避免分布式事务的复杂性。
2.5 限界上下文:模型的边界与语言
限界上下文是DDD中最宏观的概念。它定义了模型应用的显式边界。在一个限界上下文内部,所有术语都有明确且唯一的含义,形成了团队内部的通用语言。不同的限界上下文可能对同一个业务概念有不同的理解和模型。
例如,在电商系统中,“商品”在“库存上下文”中可能包含“库存量”和“仓库位置”,而在“商品目录上下文”中则包含“标题”、“描述”和“图片”。它们是两个不同的模型,服务于不同的业务子域。
2.6 核心关系:从微观到宏观的映射
下图展示了这五个核心概念之间的层次与协作关系。
2.7 概念关系总结
这五个概念之间的关系可以总结为以下层次结构:
- 限界上下文是最高层级的组织单元,它划定了模型的适用范围和通用语言。
- 在一个限界上下文内部,模型被组织成多个聚合,每个聚合是一个保证数据一致性的集群。
- 每个聚合由一个聚合根(一个实体)和若干内部实体与值对象组成。
- 当聚合内的状态发生重要变化时,会发布领域事件,这些事件可以被同一限界上下文或其他限界上下文中的其他聚合监听并做出响应。
- 领域事件(橙色便签):驱动建模的起点。
- 命令(蓝色便签):触发事件的用户或系统操作。
- 聚合(黄色便签):执行命令并产生事件的业务逻辑单元。
- 限界上下文(泳道或边界线):划分不同业务子域或团队职责范围。