领域驱动设计Skill domain-driven-design

领域驱动设计(DDD)是一种软件设计方法论,专注于建模复杂业务领域,应用战术和战略模式如实体、值对象、聚合、有界上下文和一致性策略。它用于设计软件架构,确保业务规则正确实现,通过定义通用语言、聚合边界和数据一致性来优化系统可维护性和扩展性。关键词包括:领域驱动设计,DDD,软件架构,业务建模,实体,值对象,聚合,有界上下文,领域事件,一致性策略。

架构设计 0 次安装 0 次浏览 更新于 3/19/2026

名称:领域驱动设计 描述:领域驱动设计(DDD)的战术和战略模式,包括实体、值对象、聚合、有界上下文和一致性策略。用于建模业务领域、设计聚合边界、实现业务规则或规划数据一致性时使用。

身份

您是一名领域建模专家,应用DDD战术和战略模式为复杂业务领域设计有界上下文、聚合和一致性策略。

约束

约束 {
  要求 {
    从小聚合开始,仅在不变式要求时扩展
    在建模前定义通用语言——术语必须与领域专家词汇匹配
    使值对象不可变,基于属性相等
    使用领域事件进行跨聚合一致性
  }
  从不 {
    创建贫血域模型——业务逻辑应位于领域对象内部,而非服务中
    通过对象引用其他聚合——仅使用身份引用
    在单个事务中更新多个聚合
    对领域概念使用原始类型——封装为值对象
  }
}

愿景

在建模领域前,阅读并内化:

  1. 项目CLAUDE.md ——架构、约定、优先级
  2. docs/specs/中的相关规范文档——需求和领域上下文
  3. 项目根目录的CONSTITUTION.md——如果存在,约束架构选择
  4. 现有领域模型——理解当前有界上下文和模式

何时激活

  • 建模业务领域和实体
  • 设计聚合边界
  • 实现复杂业务规则
  • 规划数据一致性策略
  • 建立有界上下文
  • 设计领域事件和集成

战略模式

有界上下文

有界上下文定义了领域模型适用的边界。同一术语在不同上下文中可能有不同含义。

示例:不同上下文中的“客户”

┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│    销售         │  │    支持         │  │    计费         │
│    上下文       │  │    上下文       │  │    上下文       │
├─────────────────┤  ├─────────────────┤  ├─────────────────┤
│ 客户:          │  │ 客户:          │  │ 客户:          │
│ - 线索          │  │ - 工单          │  │ - 发票          │
│ - 机会          │  │ - 服务级别协议  │  │ - 支付          │
│ - 提案          │  │ - 满意度        │  │ - 信用额度      │
└─────────────────┘  └─────────────────┘  └─────────────────┘

上下文识别

通过以下问题找到上下文边界:

  • 通用语言在何处变化?
  • 哪些团队拥有哪些概念?
  • 集成点自然出现在何处?
  • 哪些可以独立部署?

上下文映射

定义有界上下文如何集成:

模式 描述 使用场景
共享内核 上下文间的共享代码 紧密协作,同一团队
客户-供应商 上游/下游关系 明确的依赖方向
顺从者 下游采用上游模型 无谈判权
防腐蚀层 模型间的翻译层 保护领域免受外部模型影响
开放主机服务 发布API用于集成 多个消费者
发布语言 共享交换格式 行业标准存在时

通用语言

开发者和领域专家之间的共享词汇:

构建通用语言:

1. 从领域专家对话中提取术语
2. 用精确定义记录在词汇表中
3. 在代码中强制执行——类名、方法名、变量
4. 随着理解加深而演进

示例词汇表条目:
┌─────────────────────────────────────────────────────────────┐
│ 术语:订单                                                   │
│ 定义:客户确认购买一个或多个产品,按商定价格的请求。       │
│ 非:购物车(是意图,非订单)                                 │
│ 上下文:销售                                                 │
└─────────────────────────────────────────────────────────────┘

战术模式

实体

具有身份的随时间持续的对象。相等性基于身份,而非属性。

特征:
- 具有唯一标识符
- 可变状态
- 生命周期(创建、修改、归档)
- 基于ID的相等性

示例:
┌─────────────────────────────────────────┐
│ 实体:订单                               │
├─────────────────────────────────────────┤
│ 身份:orderId(UUID)                    │
│ 状态:状态、项目、总计                   │
│ 行为:addItem()、submit()、cancel()     │
└─────────────────────────────────────────┘

class Order {
  private readonly id: OrderId;      // 身份 - 不可变
  private status: OrderStatus;        // 状态 - 可变
  private items: OrderItem[];         // 状态 - 可变

  constructor(id: OrderId) {
    this.id = id;
    this.status = OrderStatus.Draft;
    this.items = [];
  }

  equals(other: Order): boolean {
    return this.id.equals(other.id);  // 基于身份的相等性
  }
}

值对象

无身份的对象。相等性基于属性。始终不可变。

特征:
- 无唯一标识符
- 不可变(所有属性只读)
- 基于属性的相等性
- 自我验证

示例:
┌─────────────────────────────────────────┐
│ 值对象:金钱                             │
├─────────────────────────────────────────┤
│ 属性:金额、货币                         │
│ 行为:add()、subtract()、format()       │
│ 不变式:金额 >= 0                        │
└─────────────────────────────────────────┘

class Money {
  constructor(
    public readonly amount: number,
    public readonly currency: Currency
  ) {
    if (amount < 0) throw new Error('金额不能为负');
  }

  add(other: Money): Money {
    if (!this.currency.equals(other.currency)) {
      throw new Error('不能添加不同货币');
    }
    return new Money(this.amount + other.amount, this.currency);
  }

  equals(other: Money): boolean {
    return this.amount === other.amount &&
           this.currency.equals(other.currency);
  }
}

何时使用值对象

使用值对象 使用实体
无需随时间跟踪 需要跟踪生命周期
实例可互换 唯一身份重要
由属性定义 由连续性定义
示例:金钱、地址、日期范围 示例:用户、订单、账户

聚合

具有定义边界的实体和值对象集群。一个实体是聚合根。

聚合设计规则:

1. 在聚合边界保护不变式
2. 仅通过身份引用其他聚合
3. 每个事务更新一个聚合
4. 设计小聚合(偏好单个实体)

示例:
┌─────────────────────────────────────────────────────────────┐
│ 聚合:订单                                                   │
│ 根:订单(实体)                                             │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────────┐                                        │
│  │ 订单(根)      │◄── 聚合根                               │
│  │ - orderId       │                                        │
│  │ - customerId ───┼──► 仅通过ID引用                        │
│  │ - 状态          │                                        │
│  └────────┬────────┘                                        │
│           │                                                 │
│  ┌────────▼────────┐                                        │
│  │ 订单项目        │◄── 聚合内                               │
│  │ - productId ────┼──► 仅通过ID引用                        │
│  │ - 数量          │                                        │
│  │ - 价格(金钱)  │◄── 值对象                               │
│  └─────────────────┘                                        │
└─────────────────────────────────────────────────────────────┘

聚合大小

从小开始:
- 以单实体聚合开始
- 仅当不变式要求时扩展

聚合过大的迹象:
- 频繁的乐观锁冲突
- 为简单操作加载过多数据
- 多用户同时编辑
- 跨不相关数据的交易失败

聚合过小的迹象:
- 不变式未受保护
- 业务规则分散在服务中
- 需要即时一致性时使用最终一致性

领域事件

表示域中发生的事件。关于过去的不可变事实。

事件结构:
┌─────────────────────────────────────────┐
│ 事件:订单已放置                         │
├─────────────────────────────────────────┤
│ eventId:UUID                           │
│ occurredAt:DateTime                    │
│ aggregateId:orderId                    │
│ payload:                               │
│   - customerId                          │
│   - items                               │
│   - totalAmount                         │
└─────────────────────────────────────────┘

命名约定:
- 过去时(OrderPlaced,非PlaceOrder)
- 领域语言(非技术性)
- 包含所有相关数据(事件不可变)

class OrderPlaced implements DomainEvent {
  readonly eventId = uuid();
  readonly occurredAt = new Date();

  constructor(
    readonly orderId: OrderId,
    readonly customerId: CustomerId,
    readonly items: OrderItemData[],
    readonly totalAmount: Money
  ) {}
}

事件模式

模式 描述 使用案例
事件通知 最小数据,查询细节 松散耦合
事件携带状态 事件中包含完整数据 性能、离线
事件溯源 事件作为真理来源 审计、时间查询

仓库

抽象持久化,提供类似集合的聚合访问。

仓库原则:
- 每个聚合一个仓库
- 仅返回聚合根
- 隐藏持久化机制
- 支持聚合重构

interface OrderRepository {
  findById(id: OrderId): Promise<Order | null>;
  findByCustomer(customerId: CustomerId): Promise<Order[]>;
  save(order: Order): Promise<void>;
  delete(order: Order): Promise<void>;
}

// 实现隐藏持久化细节
class PostgresOrderRepository implements OrderRepository {
  async findById(id: OrderId): Promise<Order | null> {
    const row = await this.db.query('SELECT * FROM orders WHERE id = $1', [id]);
    return row ? this.reconstitute(row) : null;
  }

  private reconstitute(row: OrderRow): Order {
    // 从持久化重建聚合
  }
}

一致性策略

交易一致性(ACID)

用于聚合内的不变式:

规则:每个交易一个聚合

// 好:单聚合更新
async function addItemToOrder(orderId: OrderId, item: OrderItem) {
  const order = await orderRepo.findById(orderId);
  order.addItem(item);  // 业务规则强制执行
  await orderRepo.save(order);
}

// 坏:单交易中多聚合
async function createOrderWithInventory() {
  await db.transaction(async (tx) => {
    await orderRepo.save(order, tx);
    await inventoryRepo.decrement(productId, quantity, tx);  // 不要这样做
  });
}

最终一致性

用于跨聚合一致性:

模式:领域事件 + 处理程序

// 订单聚合发布事件
class Order {
  submit(): void {
    this.status = OrderStatus.Placed;
    this.addEvent(new OrderPlaced(this.id, this.customerId, this.items));
  }
}

// 独立处理程序更新库存(最终)
class InventoryHandler {
  async handle(event: OrderPlaced): Promise<void> {
    for (const item of event.items) {
      await this.inventoryService.reserve(item.productId, item.quantity);
    }
  }
}

Saga模式

通过补偿协调多个聚合:

Saga:订单履行

┌─────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────┐
│ 创建    │────►│ 预留        │────►│ 收费        │────►│ 发货    │
│ 订单    │     │ 库存        │     │ 支付        │     │ 订单    │
└────┬────┘     └──────┬──────┘     └──────┬──────┘     └─────────┘
     │                 │                   │
     │ 补偿:          │ 补偿:            │ 补偿:
     │ 取消订单        │ 释放库存          │ 退款支付
     ▼                 ▼                   ▼

在任何步骤失败时,按相反顺序执行补偿。

选择一致性

场景 策略
单聚合内 交易(ACID)
同一服务中的跨聚合 最终(领域事件)
跨服务 Saga带补偿
读模型更新 最终(投影)

反模式

贫血域模型

// 反模式:逻辑在域对象外
class Order {
  id: string;
  items: Item[];
  status: string;
}

class OrderService {
  calculateTotal(order: Order): number { ... }
  validate(order: Order): boolean { ... }
  submit(order: Order): void { ... }
}

// 更好:逻辑在域对象内
class Order {
  private items: OrderItem[];
  private status: OrderStatus;

  get total(): Money {
    return this.items.reduce((sum, item) => sum.add(item.subtotal), Money.zero());
  }

  submit(): void {
    this.validate();
    this.status = OrderStatus.Submitted;
  }
}

大型聚合

// 反模式:所有内容在一个聚合中
class Customer {
  orders: Order[];           // 可能成千上万
  addresses: Address[];
  paymentMethods: PaymentMethod[];
  preferences: Preferences;
  activityLog: Activity[];   // 可能数百万
}

// 更好:通过ID引用的分离聚合
class Customer {
  id: CustomerId;
  defaultAddressId: AddressId;
  defaultPaymentMethodId: PaymentMethodId;
}

class Order {
  customerId: CustomerId;    // 通过ID引用
}

原始类型痴迷

// 反模式:对领域概念使用原始类型
function createOrder(
  customerId: string,
  productId: string,
  quantity: number,
  price: number,
  currency: string
) { ... }

// 更好:值对象
function createOrder(
  customerId: CustomerId,
  productId: ProductId,
  quantity: Quantity,
  price: Money
) { ... }

实现检查清单

聚合设计

  • [ ] 单实体可作为聚合根
  • [ ] 不变式在边界受保护
  • [ ] 其他聚合仅通过ID引用
  • [ ] 内存中适配合
  • [ ] 每个聚合一个交易

实体实现

  • [ ] 具有唯一标识符
  • [ ] 相等性基于ID
  • [ ] 封装业务规则
  • [ ] 状态通过方法改变

值对象实现

  • [ ] 所有属性不可变
  • [ ] 相等性基于属性
  • [ ] 自我验证
  • [ ] 操作返回新实例

仓库实现

  • [ ] 每个聚合一个
  • [ ] 仅返回聚合根
  • [ ] 隐藏持久化细节
  • [ ] 支持域所需的查询

参考