强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

微服务拆分精讲 / 第 03 章:领域驱动设计

第 03 章:领域驱动设计

DDD 不是技术,而是思维方式。它教我们用业务语言来设计系统,用限界上下文来划分边界。


3.1 DDD 核心概念

3.1.1 什么是领域驱动设计

领域驱动设计(Domain-Driven Design,DDD)是 Eric Evans 在 2003 年提出的一种软件设计方法论。它的核心思想是:

软件设计的核心复杂性在于业务领域,而非技术实现。

┌──────────────────────────────────────────────────────┐
│                   DDD 分层架构                        │
├──────────────────────────────────────────────────────┤
│                                                      │
│  ┌───────────────────────────────────────────────┐  │
│  │           用户接口层 (Interface Layer)          │  │
│  │    Controller · DTO · API Gateway              │  │
│  └──────────────────────┬────────────────────────┘  │
│                         ▼                            │
│  ┌───────────────────────────────────────────────┐  │
│  │           应用层 (Application Layer)            │  │
│  │    Application Service · 编排 · 事务管理        │  │
│  └──────────────────────┬────────────────────────┘  │
│                         ▼                            │
│  ┌───────────────────────────────────────────────┐  │
│  │           领域层 (Domain Layer) ⭐ 核心          │  │
│  │    Entity · Value Object · Aggregate           │  │
│  │    Domain Service · Domain Event               │  │
│  └──────────────────────┬────────────────────────┘  │
│                         ▼                            │
│  ┌───────────────────────────────────────────────┐  │
│  │          基础设施层 (Infrastructure Layer)       │  │
│  │    Repository · ORM · MQ · External API        │  │
│  └───────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────┘

3.1.2 DDD 战略设计 vs 战术设计

维度战略设计(Strategic)战术设计(Tactical)
关注点业务边界划分代码结构设计
核心概念限界上下文、上下文映射聚合、实体、值对象
参与者架构师 + 业务专家开发团队
输出物上下文地图、服务划分方案领域模型代码
与微服务的关系指导服务拆分指导服务内部设计

3.1.3 统一语言(Ubiquitous Language)

统一语言是 DDD 的基石——开发团队和业务专家使用同一套术语进行沟通。

  ❌ 开发人员和业务人员说不同的话

  开发:"我在 user 表加了一个 status 字段,0 表示未激活..."
  业务:"什么 status?我们叫'客户状态',有'新注册'、'已认证'、'已冻结'..."

  ✅ 统一语言:双方使用相同的术语

  术语表:
  ┌──────────────┬────────────────────────────┐
  │ 术语         │ 含义                        │
  ├──────────────┼────────────────────────────┤
  │ 客户 (Customer) │ 已完成注册并通过认证的用户  │
  │ 客户状态     │ 新注册 / 已认证 / 已冻结     │
  │ 订单 (Order) │ 客户发起的购买请求           │
  │ 订单项 (LineItem) │ 订单中的一个商品及数量   │
  └──────────────┴────────────────────────────┘

💡 实践建议:在项目开始时建立术语表(Glossary),并在整个团队中推广使用。代码中的类名、方法名应该直接使用统一语言中的术语。


3.2 限界上下文(Bounded Context)

3.2.1 概念

限界上下文是 DDD 中最重要的概念,也是微服务拆分的核心依据。

限界上下文是一个边界,在这个边界内,领域模型的每个术语都有明确且唯一的含义。

┌─────────────── 电商系统 ───────────────────────┐
│                                                │
│  ┌─────────────────┐  ┌─────────────────┐     │
│  │   销售上下文     │  │   物流上下文     │     │
│  │                 │  │                 │     │
│  │ "订单" = 购买   │  │ "订单" = 发货   │     │
│  │  请求,包含     │  │  指令,包含     │     │
│  │  商品、金额     │  │  地址、包裹     │     │
│  └─────────────────┘  └─────────────────┘     │
│                                                │
│  ┌─────────────────┐  ┌─────────────────┐     │
│  │   客户上下文     │  │   支付上下文     │     │
│  │                 │  │                 │     │
│  │ "客户" = 已注册  │  │ "客户" = 付款方 │     │
│  │  的用户,有     │  │  有账户余额     │     │
│  │  会员等级       │  │  和支付方式     │     │
│  └─────────────────┘  └─────────────────┘     │
└────────────────────────────────────────────────┘

同一个词 "订单"、"客户" 在不同上下文中含义不同!

3.2.2 识别限界上下文的方法

方法操作步骤输出
事件风暴(Event Storming)组织工作坊,用便利贴梳理业务事件领域事件序列
名词提取法从业务文档中提取名词,分析聚类实体/概念分组
业务流程分析梳理核心业务流程,识别阶段边界流程阶段划分
组织架构映射参考部门/团队职责划分组织-领域对应

3.2.3 事件风暴(Event Storming)实操

事件风暴是 Alberto Brandolini 发明的协作式建模方法:

  时间轴 ──────────────────────────────────────────▶

  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
  │ 🟡 命令  │    │ 🟡 命令  │    │ 🟡 命令  │    │ 🟡 命令  │
  │ 提交订单 │    │ 支付订单 │    │ 发货     │    │ 确认收货 │
  └────┬────┘    └────┬────┘    └────┬────┘    └────┬────┘
       ▼              ▼              ▼              ▼
  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
  │ 🟠 事件  │    │ 🟠 事件  │    │ 🟠 事件  │    │ 🟠 事件  │
  │ 订单已   │    │ 支付已   │    │ 订单已   │    │ 订单已   │
  │ 提交     │    │ 完成     │    │ 发货     │    │ 完成     │
  └────┬────┘    └────┬────┘    └────┬────┘    └────┬────┘
       ▼              ▼              ▼              ▼
  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
  │ 🔵 聚合  │    │ 🔵 聚合  │    │ 🔵 聚合  │    │ 🔵 聚合  │
  │  订单    │    │  支付    │    │  物流    │    │  订单    │
  └─────────┘    └─────────┘    └─────────┘    └─────────┘
       │              │              │
       ▼              ▼              ▼
  ┌─────────┐    ┌─────────┐    ┌─────────┐
  │ 🟣 上下文│    │ 🟣 上下文│    │ 🟣 上下文│
  │ 交易上下文│    │ 支付上下文│    │ 物流上下文│
  └─────────┘    └─────────┘    └─────────┘

操作步骤

  1. 召集工作坊:业务专家 + 开发团队(5-10 人)
  2. 梳理领域事件(橙色便利贴):用过去时描述,如"订单已提交"
  3. 识别命令(蓝色便利贴):触发事件的操作,如"提交订单"
  4. 标注聚合(黄色便利贴):处理命令的核心实体
  5. 划定边界(粉色线):将相关事件/聚合归入同一上下文

3.2.4 上下文映射(Context Mapping)

不同限界上下文之间的关系模式:

映射模式英文说明示例
合作关系Partnership两个上下文紧密合作,共同演进用户服务 ↔ 会员服务
共享内核Shared Kernel两个上下文共享部分模型共享用户基础信息
客户-供应商Customer-Supplier上游提供,下游消费商品服务(供)→ 订单服务(客)
防腐层Anti-Corruption Layer通过翻译层隔离外部模型遗留系统接入层
开放主机服务Open Host Service提供标准化协议供多方使用开放 API
遵从者Conformist下游完全遵从上游模型使用第三方 SDK
各行其道Separate Ways两个上下文完全独立推荐系统 ↔ 客服系统
┌──────────────┐    客户-供应商     ┌──────────────┐
│  商品上下文   │ ──────────────▶  │  订单上下文   │
│  (Supplier)   │                  │  (Customer)   │
└──────────────┘                  └──────┬───────┘
                                         │
                                    防腐层│(ACL)
                                         ▼
                                  ┌──────────────┐
                                  │  物流上下文    │
                                  │ (Legacy系统)  │
                                  └──────────────┘

3.3 聚合根(Aggregate Root)

3.3.1 概念

聚合(Aggregate)是一组相关对象的集合,作为数据变更的一致性边界。聚合根(Aggregate Root)是聚合的入口点,外部只能通过聚合根访问聚合内部的对象。

  ┌──────────────────────────────────────────────┐
  │              聚合:订单 (Order)                │
  │              聚合根:Order                     │
  │                                              │
  │   ┌──────────┐                               │
  │   │  Order   │ ◀── 外部只能通过 Order 访问     │
  │   │ (聚合根) │                               │
  │   └────┬─────┘                               │
  │        │                                     │
  │        ├──── OrderItem (实体)                 │
  │        │     ├─ productId                     │
  │        │     ├─ quantity                      │
  │        │     └─ price                        │
  │        │                                     │
  │        ├──── ShippingAddress (值对象)          │
  │        │     ├─ province                      │
  │        │     ├─ city                          │
  │        │     └─ detail                        │
  │        │                                     │
  │        └──── PaymentInfo (值对象)              │
  │              ├─ method                        │
  │              └─ transactionId                  │
  └──────────────────────────────────────────────┘

3.3.2 聚合设计原则

原则说明违反后果
聚合边界保护一致性聚合内的事务必须强一致数据不一致
通过 ID 引用其他聚合聚合之间不直接持有对象引用聚合边界模糊
一个事务只修改一个聚合跨聚合操作通过领域事件分布式事务难题
聚合根控制访问外部不能直接修改聚合内部对象一致性失控
小聚合优先聚合尽量小,只包含必要元素性能和并发问题

3.3.3 聚合设计实例

  ❌ 错误:聚合过大

  ┌──────────────────────────────────────────┐
  │  Order (聚合根)                           │
  │  ├─ OrderItems[]                         │
  │  ├─ Customer (整个客户对象) ← 不应该!    │
  │  │   ├─ name, email                      │
  │  │   ├─ addresses[]                      │
  │  │   ├─ paymentMethods[]                 │
  │  │   └─ membershipLevel                  │
  │  └─ Product (整个商品对象) ← 不应该!     │
  │      ├─ name, description                │
  │      ├─ images[]                         │
  │      └─ inventory                        │
  └──────────────────────────────────────────┘

  ✅ 正确:通过 ID 引用

  ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
  │ Order       │     │ Customer    │     │ Product     │
  │ (聚合根)     │     │ (聚合根)     │     │ (聚合根)     │
  │             │     │             │     │             │
  │ customerId──┼──▶  │ id, name    │     │ id, name    │
  │             │     │ email       │     │ price       │
  │ items[]     │     │             │     │             │
  │ ├ productId─┼──▶  │             │     │             │
  │ ├ quantity  │     └─────────────┘     └─────────────┘
  │ └ price     │
  │ address     │
  └─────────────┘

3.4 领域事件(Domain Event)

3.4.1 概念

领域事件表示领域中发生的、业务专家关心的事情。它是聚合之间、限界上下文之间通信的主要方式。

  命令 (Command)          领域事件 (Domain Event)
  ─────────────           ────────────────────
  提交订单         ──▶     OrderSubmitted
  支付完成         ──▶     PaymentCompleted
  商品已发货       ──▶     OrderShipped
  库存不足         ──▶     StockInsufficient

3.4.2 领域事件命名规范

规范✅ 正确❌ 错误
使用过去时OrderSubmittedSubmitOrder / OrderSubmit
业务语言PaymentCompletedPaymentStatusChanged
具体而非笼统OrderCancelledDataModified
不含技术术语CustomerRegisteredUserInsertedToDB

3.4.3 领域事件驱动的服务协作

  ┌──────────┐         ┌──────────┐         ┌──────────┐
  │ 订单服务  │         │ 支付服务  │         │ 库存服务  │
  │          │         │          │         │          │
  │ 创建订单  │         │          │         │          │
  │    │     │         │          │         │          │
  │    ▼     │         │          │         │          │
  │ 发布事件: │         │          │         │          │
  │ Order    │         │          │         │          │
  │ Submitted│         │          │         │          │
  └────┬─────┘         └──────────┘         └──────────┘
       │                                        │
       │          ┌──── 消息队列 ────┐           │
       ├─────────▶│ OrderSubmitted   │◀──────────┤
       │          └──────────────────┘           │
       │                │                        │
       │                ▼                        │
       │         ┌──────────┐                   │
       │         │ 支付服务  │                   │
       │         │ 创建支付  │                   │
       │         │ 记录      │                   │
       │         └────┬─────┘                   │
       │              │                         │
       │              ▼                         │
       │         发布事件:                       │
       │         Payment                        │
       │         Completed                      │
       │              │                         │
       │              ▼                         ▼
       │         ┌──────────┐          ┌──────────┐
       │         │ 订单服务  │          │ 库存服务  │
       │         │ 更新订单  │          │ 扣减库存  │
       │         │ 状态      │          │          │
       │         └──────────┘          └──────────┘

3.4.4 事件的结构

{
  "eventId": "evt-20260510-001",
  "eventType": "OrderSubmitted",
  "timestamp": "2026-05-10T10:30:00+08:00",
  "source": "order-service",
  "data": {
    "orderId": "ORD-20260510-001",
    "customerId": "CUST-001",
    "totalAmount": 299.00,
    "items": [
      {
        "productId": "PROD-001",
        "quantity": 2,
        "price": 149.50
      }
    ]
  },
  "metadata": {
    "correlationId": "req-abc-123",
    "version": "1.0"
  }
}

3.5 实体(Entity)与值对象(Value Object)

3.5.1 区别

维度实体 (Entity)值对象 (Value Object)
标识有唯一 ID没有 ID,通过属性值识别
可变性可变不可变
相等性ID 相同即相等所有属性相同才相等
生命周期有独立生命周期依附于实体
示例Order, Customer, ProductMoney, Address, DateRange
  实体 (Entity)
  ┌────────────────────────┐
  │ Order                   │
  │ id: "ORD-001" ← 唯一标识 │
  │ status: PAID            │ ← 可变
  │ createdAt: 2026-05-10   │
  └────────────────────────┘

  值对象 (Value Object)
  ┌────────────────────────┐
  │ Money                   │
  │ amount: 100.00          │ ← 不可变
  │ currency: "CNY"         │
  │                         │
  │ Money(100, "CNY")       │
  │   == Money(100, "CNY")  │ ← 值相等即相等
  └────────────────────────┘

3.5.2 代码示例

// 实体 - 有唯一标识
public class Order {
    private String orderId;      // 唯一标识
    private OrderStatus status;
    private List<OrderItem> items;
    private Money totalAmount;   // 值对象

    public void cancel() {
        if (this.status == OrderStatus.SHIPPED) {
            throw new BusinessException("已发货订单不能取消");
        }
        this.status = OrderStatus.CANCELLED;
    }
}

// 值对象 - 不可变,无标识
public record Money(BigDecimal amount, String currency) {
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("币种不同不能相加");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }
}

3.6 从业务场景到服务划分

3.6.1 完整案例:外卖平台

第一步:事件风暴梳理业务流程

用户浏览餐厅 → 用户选择菜品 → 用户下单 → 餐厅接单 → 骑手取餐 → 骑手送达 → 用户评价

对应的领域事件:
CustomerBrowsed → ItemSelected → OrderPlaced → RestaurantAccepted
→ RiderAssigned → FoodPickedUp → FoodDelivered → OrderReviewed

第二步:识别限界上下文

上下文包含的聚合核心职责
用户上下文Customer, Address用户注册、认证、地址管理
餐厅上下文Restaurant, Menu, Dish餐厅入驻、菜单管理
订单上下文Order, OrderItem下单、订单状态管理
配送上下文Rider, Delivery骑手管理、配送调度
支付上下文Payment, Refund支付处理、退款
评价上下文Review, Rating用户评价

第三步:映射为微服务

┌──────────┐  ┌──────────┐  ┌──────────┐
│ 用户服务  │  │ 餐厅服务  │  │ 订单服务  │
│          │  │          │  │          │
│ Customer │  │ Restaurant│  │ Order    │
│ Address  │  │ Menu     │  │ OrderItem│
│          │  │ Dish     │  │          │
└──────────┘  └──────────┘  └──────────┘

┌──────────┐  ┌──────────┐  ┌──────────┐
│ 配送服务  │  │ 支付服务  │  │ 评价服务  │
│          │  │          │  │          │
│ Rider    │  │ Payment  │  │ Review   │
│ Delivery │  │ Refund   │  │ Rating   │
└──────────┘  └──────────┘  └──────────┘

3.6.2 上下文协作流程

  用户下单的完整流程:

  用户服务        订单服务        支付服务        餐厅服务        配送服务
     │               │               │               │               │
     │  创建订单      │               │               │               │
     │──────────────▶│               │               │               │
     │               │               │               │               │
     │               │  创建支付      │               │               │
     │               │──────────────▶│               │               │
     │               │               │               │               │
     │               │  PaymentCompleted              │               │
     │               │◀──────────────│               │               │
     │               │               │               │               │
     │               │  OrderPaid     │               │               │
     │               │──────────────────────────────▶│               │
     │               │               │               │               │
     │               │               │  RestaurantAccepted            │
     │               │◀──────────────────────────────│               │
     │               │               │               │               │
     │               │  OrderReady    │               │               │
     │               │──────────────────────────────────────────────▶│
     │               │               │               │               │
     │               │               │               │  DeliveryDone │
     │               │◀──────────────────────────────────────────────│
     │               │               │               │               │
     │  OrderCompleted               │               │               │
     │◀──────────────│               │               │               │

3.7 DDD 工具与框架

语言/平台框架说明
JavaAxon FrameworkCQRS + Event Sourcing 框架
JavaSpring Modulith模块化单体 + DDD
.NETeShopOnContainers微服务参考实现
Gogo-ddd-skeletonDDD 项目骨架
Pythoneventsourcing事件溯源库

⚠️ 注意事项

  1. DDD 不是万能的——简单 CRUD 业务不需要 DDD
  2. 限界上下文 ≠ 微服务——一个限界上下文可以包含多个服务
  3. 不要跳过事件风暴——直接画技术架构图会遗漏业务细节
  4. 聚合设计要谨慎——聚合过大导致性能问题,过小导致分布式事务
  5. 统一语言要落地——代码中的命名必须与统一语言一致

📖 扩展阅读

  1. Eric Evans - Domain-Driven Design: Tackling Complexity in the Heart of Software — DDD 圣经
  2. Vaughn Vernon - Implementing Domain-Driven Design — DDD 实践指南
  3. Alberto Brandolini - Introducing EventStorming — 事件风暴方法论
  4. Martin Fowler - BoundedContext — 限界上下文详解
  5. Chris Richardson - Microservices Patterns, Chapter 4 — DDD 与微服务结合

本章小结

要点说明
DDD 核心以业务领域为中心设计软件
限界上下文微服务拆分的核心依据
聚合根数据一致性的边界
领域事件服务间通信的推荐方式
事件风暴识别上下文的协作式方法

📌 下一章第 04 章:拆分策略 — 将 DDD 理论落地为可操作的拆分策略。