领域驱动设计(DDD)
DDD 是一种以业务领域为核心的软件设计方法,通过统一语言连接业务与技术,解决复杂业务系统的建模难题。
1. DDD 核心思想
┌────────────────────────────────┐
│ 统一语言 Ubiquitous │
│ Language │
└────────────────────────────────┘
↓ ↑
┌────────────────────────────────┐
│ 领域模型 Domain Model │
│ ┌─────┐ ┌─────┐ ┌────────┐ │
│ │实体 │ │值对象│ │ 聚合 │ │
│ └─────┘ └─────┘ └────────┘ │
└────────────────────────────────┘
核心:技术与业务使用同一套语言进行沟通,模型直接反映业务概念。
2. 战略设计
子域(Subdomain)
将复杂业务划分为不同类型:
| 类型 | 特征 | 示例 |
|---|---|---|
| 核心域 | 竞争优势所在,投入最多 | 电商的推荐算法 |
| 支撑子域 | 需要定制但不独特 | 订单管理 |
| 通用子域 | 可外购或标准化 | 用户认证、通知服务 |
限界上下文(Bounded Context)
模型一致性的边界。同一个概念在不同上下文可以有不同的含义。
┌─────────────────┐ ┌─────────────────┐
│ 商品上下文 │ │ 库存上下文 │
│ Product │ │ Product │
│ - name │ │ - sku │
│ - description │ │ - quantity │
│ - price │ │ - warehouse │
│ - category │ │ - location │
└─────────────────┘ └─────────────────┘
↑ ↑
「商品」是销售实体 「商品」是库存单位
上下文映射
┌──────────────┐ 合作关系 ┌──────────────┐
│ 订单上下文 │ ←─────────────→ │ 支付上下文 │
└──────────────┘ └──────────────┘
│
│ 上游/供应商
↓
┌──────────────┐
│ 库存上下文 │
└──────────────┘
| 关系 | 含义 |
|---|---|
| 合作关系 | 任一方失败影响另一方 |
| 客户-供应商 | 上游优先满足下游需求 |
| 跟随者 | 复用上游模型 |
| 防腐层 (ACL) | 隔离外部模型影响 |
| 开放主机 | 定义清晰接口供外部使用 |
3. 战术设计
实体(Entity)
有唯一标识且通过标识区分的对象,生命周期中属性可变。
public class Order {
private OrderId id; // 唯一标识
private CustomerId customerId;
private List<OrderItem> items;
private OrderStatus status;
public void pay() { // 业务行为
if (status != PENDING) throw new IllegalStateException();
this.status = PAID;
}
public void ship() { ... }
}
值对象(Value Object)
属性决定身份,不可变,无生命周期概念。
public record Money(BigDecimal amount, Currency currency) {
public Money add(Money other) {
if (!this.currency.equals(other.currency))
throw new IllegalArgumentException("Currency mismatch");
return new Money(this.amount.add(other.amount), this.currency);
}
}
public record Address(
String province,
String city,
String street
) {}
聚合(Aggregate)
一致性边界内的实体与值对象集群。
// Order 是聚合根(Aggregate Root)
public class Order {
private OrderId id;
private List<OrderItem> items; // 聚合内实体
private ShippingAddress address; // 值对象
// 所有修改通过聚合根
public void addItem(ProductId productId, int quantity, Money price) {
items.add(new OrderItem(productId, quantity, price));
recalculateTotal();
}
}
聚合设计原则:
- 一个事务只修改一个聚合
- 聚合根负责维护不变性
- 聚合间通过 ID 引用(避免直接对象引用)
领域服务(Domain Service)
不属于某个聚合的业务逻辑。
public class PricingService {
public Money calculateDiscount(Order order, Customer customer) {
if (customer.isVIP()) {
return order.getTotal().multiply(0.8);
}
return order.getTotal();
}
}
领域事件(Domain Event)
领域内发生的有意义的事情,实现最终一致性。
public class OrderCreatedEvent {
private final OrderId orderId;
private final CustomerId customerId;
private final Instant occurredOn;
// 用于其他上下文处理后续业务
}
// 发布
public class Order {
public void create() {
// ...
registerEvent(new OrderCreatedEvent(this.id, ...));
}
}
4. 事件风暴(Event Storming)
Alberto Brandolini 发明的协作建模工作坊。
流程
1. 领域事件(橙色) → OrderCreated, PaymentReceived
2. 命令(蓝色) → CreateOrder, ReceivePayment
3. 聚合(黄色) → Order, Payment
4. 策略(紫色) → 如果支付失败则取消订单
5. 外部系统(粉色) → 支付网关、物流系统
产出物
- 限界上下文划分
- 聚合边界
- 领域事件流
- 服务拆分候选
5. DDD 分层架构
┌─────────────────────────────────────┐
│ 用户接口层(UI/API) │
│ 适配器模式、DTO、输入校验 │
├─────────────────────────────────────┤
│ 应用层(Application) │
│ 用例编排、事务控制、事件发布 │
├─────────────────────────────────────┤
│ 领域层(Domain)← 核心 │
│ 实体、值对象、聚合、领域服务、事件 │
├─────────────────────────────────────┤
│ 基础设施层(Infrastructure) │
│ 数据库、消息队列、外部 API 适配 │
└─────────────────────────────────────┘
依赖方向:外层依赖内层,领域层无外部依赖。
6. DDD 与微服务
限界上下文 → 微服务边界
聚合根 → 服务内模块
领域事件 → 服务间通信
防腐层 → API Gateway / Adapter
| DDD 概念 | 映射微服务 |
|---|---|
| 核心域 | 独立团队维护的核心服务 |
| 通用子域 | 共享基础设施服务 |
| 限界上下文 | 服务部署边界 |
| 领域事件 | 事件驱动架构 |
总结
| 层面 | 关键产出 | 工具 |
|---|---|---|
| 战略设计 | 子域划分、限界上下文 | 事件风暴 |
| 战术设计 | 聚合、实体、领域服务 | 代码模型 |
| 技术实践 | 分层架构、事件驱动 | 框架实现 |
DDD 的价值不在于严格按照名词定义编码,而在于培养以业务领域为核心思考问题的能力。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。