领域驱动设计基础:实体、值对象、聚合、聚合根、领域事件的核心概念回顾
在深入支付订单系统的聚合建模之前,必须重新审视领域驱动设计的五个核心构造块。这些概念不仅是战术建模的基石,更是决定微服务边界、数据一致性和缓存策略的关键。以下逐一拆解其定义、特征及在支付场景中的映射。
1. 实体(Entity)
实体是具有唯一标识且会随时间变化的对象。其核心特征是身份连续性——即使所有属性都改变,标识依然不变。
- 标识方式:自然主键(如订单号)、系统生成ID(如雪花算法ID)、业务组合键。
- 可变性:属性可修改,但标识不可变。
- 相等性:通过标识判断相等,而非属性值。
支付场景示例: 订单实体(Order)——订单号“ORD20250321001”始终代表同一笔交易,即使其状态从“待支付”变为“已支付”,地址被修改,它仍然是同一个订单。
2. 值对象(Value Object)
值对象通过属性值来定义自身,没有唯一标识,且不可变。当属性值完全相同时,两个值对象被视为相等。
| 特征 | 实体 | 值对象 |
|---|---|---|
| 标识 | 有唯一标识 | 无标识 |
| 可变性 | 可变 | 不可变(替换而非修改) |
| 相等性 | 标识相等 | 属性值全等 |
| 生命周期 | 独立生命周期 | 依附于实体 |
| 支付案例 | 订单、支付单、账户 | 金额、地址、货币类型 |
设计原则: 优先使用值对象而非基本类型。例如,将“BigDecimal amount + String currency”封装为
Money 值对象,可避免精度丢失和货币单位混淆。
3. 聚合(Aggregate)与聚合根(Aggregate Root)
聚合是领域对象的一致性边界,它将一组关联的实体和值对象封装为一个整体。聚合根是聚合中唯一允许外部访问的入口,所有外部操作必须通过聚合根进行。
- 一致性边界:聚合内部保证事务一致性,跨聚合使用最终一致性。
- 聚合根规则:聚合根是实体,拥有全局标识;聚合内部对象只能通过聚合根访问。
- 引用方式:外部只持有聚合根的ID,不持有内部对象的引用。
常见误区: 将数据库表的一对多关系直接映射为聚合关系。例如,订单行(OrderLine)不应独立存在,而应作为订单聚合的一部分,通过订单根来管理。
以下 SVG 展示了支付订单聚合的典型结构,清晰呈现聚合根、实体与值对象的边界关系:
4. 领域事件(Domain Event)
领域事件是过去发生的、对业务有意义的事实记录。它用于解耦聚合之间的通信,是实现最终一致性的核心机制。
- 命名规范:使用过去时态,如
OrderPaid、PaymentFailed。 - 不可变性:事件一旦发布,内容不可更改。
- 发布时机:在聚合根的方法中,状态变更后立即发布。
- 存储与投递:通常先持久化事件,再通过消息中间件投递。
支付场景示例: 当订单支付成功时,订单聚合根发布
OrderPaidEvent,包含 orderId、paidAmount、paidTime。库存服务订阅该事件后扣减库存,积分服务增加积分——整个过程通过事件驱动,无需同步调用。
以下 SVG 展示了领域事件在支付流程中的流转路径:
5. 核心概念对比总结
| 概念 | 标识 | 可变性 | 生命周期 | 支付订单中的角色 |
|---|---|---|---|---|
| 实体 | 有(全局唯一) | 可变 | 独立 | 订单、支付单、退款单 |
| 值对象 | 无 | 不可变 | 依附于实体 | 金额、货币、地址、银行卡号 |
| 聚合根 | 有(全局唯一) | 可变(控制边界) | 独立(入口) | 订单(管理支付单、物流单) |
| 领域事件 | 有(事件ID) | 不可变 | 发布即存档 | OrderPaid、PaymentRefunded |
建模建议: 在支付订单系统中,将“订单”设计为聚合根,内部包含“支付单”实体和“金额”值对象。跨聚合的交互(如订单与库存)通过领域事件解耦,为后续的幂等处理和缓存策略奠定结构基础。