3. 领域驱动设计基础:实体与值对象、聚合与聚合根、领域事件、领域服务
领域驱动设计(Domain-Driven Design,DDD)为《OpenAPI 领域边界》课程提供了核心的建模方法论。在将业务需求转化为 API 契约之前,必须理解 DDD 的四个基本构造块:实体与值对象、聚合与聚合根、领域事件以及领域服务。这些概念直接决定了 API 的资源粒度、状态管理方式和边界划分。
3.1 实体与值对象
实体(Entity)和值对象(Value Object)是领域模型中最基础的两种对象类型,它们的区别在于身份标识和可变性。
| 特性 | 实体(Entity) | 值对象(Value Object) |
|---|---|---|
| 身份标识 | 拥有唯一标识(如 ID、UUID),标识贯穿生命周期不变 | 没有身份标识,由属性值定义相等性 |
| 可变性 | 可变(Mutable),状态可随时间变化 | 不可变(Immutable),创建后属性不可修改 |
| 相等性判断 | 通过标识判断(如 userId 相同即同一实体) | 通过所有属性值判断(如金额+币种相同即相等) |
| 生命周期 | 有独立生命周期,可被创建、修改、删除 | 无独立生命周期,可被整体替换或丢弃 |
| API 映射 | 通常映射为 REST 资源,拥有独立 URI | 通常作为请求体/响应体中的嵌套对象 |
API 设计提示:在 OpenAPI 规范中,实体通常对应
object 类型且包含 id 属性;值对象也对应 object 类型,但不应包含独立标识字段,且建议标记为 readOnly: true 以强调不可变性。
3.2 聚合与聚合根
聚合(Aggregate)是一组相关对象的集合,被视为数据修改的一致性边界。聚合根(Aggregate Root)是聚合中唯一允许被外部引用的实体,负责维护聚合内部的不变量。
核心原则:外部对象只能通过聚合根访问聚合内的其他对象;聚合之间的关联应使用聚合根的全局标识,而非对象引用。
聚合设计的三条关键规则:
- 一致性边界:一个事务只修改一个聚合,聚合内部保证最终一致性。
- 聚合根唯一入口:所有对聚合内对象的操作必须通过聚合根的方法进行。
- 引用通过标识:聚合之间通过 ID 引用,避免直接对象引用导致的级联遍历。
| 概念 | 职责 | API 设计影响 |
|---|---|---|
| 聚合根 | 全局标识、生命周期管理、一致性守卫 | 映射为顶级资源端点(如 /orders) |
| 聚合内部实体 | 局部标识、聚合内部行为 | 通常作为子资源或嵌套对象(如 /orders/{id}/items) |
| 聚合内部值对象 | 属性封装、无标识、不可变 | 嵌套在请求/响应体中,无独立端点 |
3.3 领域事件
领域事件(Domain Event)是领域模型中已经发生的、对业务有意义的事实记录。它用于在聚合之间或限界上下文之间传递状态变更通知,实现最终一致性。
常见误区:领域事件不是 API 请求日志,也不是系统内部的技术事件(如“数据库连接超时”)。它必须是业务人员能够理解的事实,例如“订单已支付”、“库存已不足”。
领域事件在 OpenAPI 设计中的两种典型模式:
- 事件通知(Event Notification):API 端点直接发布事件消息(如 Webhook 回调),接收方自行获取最新状态。
- 事件溯源(Event Sourcing):API 将事件作为一等资源暴露,支持查询和重放历史事件流。
3.4 领域服务
领域服务(Domain Service)用于承载那些不属于任何实体或值对象的领域逻辑。当某个业务操作涉及多个聚合或多个外部系统,且无法自然地归属于某个实体时,应将其建模为领域服务。
判断标准:如果业务逻辑需要操作多个聚合根,或者需要调用外部基础设施(如支付网关、物流 API),且该逻辑在实体中会导致“方法膨胀”或“职责不清”,则考虑提取为领域服务。
领域服务在 OpenAPI 中的映射方式:
- 无状态服务端点:映射为 POST 操作,请求体包含所有必要参数,响应体返回计算结果。
- 资源化服务:将领域服务建模为“计算资源”或“任务资源”,支持 GET 查询和 POST 提交。
# OpenAPI 3.1 领域服务示例:订单价格计算服务
paths:
/domain-services/order-pricing:
post:
summary: 计算订单最终价格(含折扣、税费、运费)
operationId: calculateOrderPrice
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/OrderItem'
customerId:
type: string
couponCode:
type: string
responses:
'200':
description: 价格计算结果
content:
application/json:
schema:
$ref: '#/components/schemas/PriceCalculationResult'
x-domain-service: true
x-aggregate-root: Order
OpenAPI 扩展标记:建议使用
x-domain-service 和 x-aggregate-root 等自定义扩展属性,在 API 契约中显式标注领域驱动设计的构造块类型,便于代码生成和文档导航。
3.5 四种构造块在 API 边界设计中的协同
在实际的 OpenAPI 设计中,这四种构造块并非孤立存在,而是共同构成领域边界:
| DDD 构造块 | API 资源/操作 | 边界职责 |
|---|---|---|
| 聚合根 | 顶级 REST 资源(如 /orders) | 定义资源生命周期和一致性边界 |
| 实体 | 子资源或嵌套对象(如 /orders/{id}/items) | 维护局部状态和标识 |
| 值对象 | 请求/响应体中的嵌套 schema | 封装不可变属性,减少冗余端点 |
| 领域事件 | Webhook 回调或事件资源端点 | 跨聚合/跨上下文异步通知 |
| 领域服务 | 无状态 POST 端点或计算资源 | 承载跨聚合的业务逻辑 |