DDD领域设计【一、简介】

领域驱动设计(Domain-Driven Design, DDD) 是由 Eric Evans 于 2003 年提出的一套应对复杂业务系统的软件开发方法论。其核心思想是 将业务领域作为软件设计的核心,通过建立精准的领域模型来分离业务复杂性与技术复杂性,最终实现代码与业务的高度一致。

DDD 主要分为两大阶段:战略设计(解决系统如何拆分、边界在哪)和 战术设计(解决代码如何组织、模型如何构建)。以下是详细的名词与思想解析。


一、DDD 核心思想

1. 核心哲学

  • 业务优先:技术为业务服务,深度理解业务是架构设计的前提。
  • 统一语言:开发、产品、业务专家使用同一套术语,消除沟通壁垒。
  • 模型驱动:通过领域模型表达业务规则,模型即业务,代码即模型。
  • 分而治之:将庞大系统拆分为高内聚、低耦合的小模块(限界上下文)。

2. 两大阶段

  • 战略设计(Strategic Design)做什么。划分业务边界、确定系统架构、明确团队分工。
  • 战术设计(Tactical Design)怎么做。在边界内构建领域模型、设计代码结构、实现业务逻辑。

二、战略设计核心名词(宏观架构)

1. 领域 (Domain)

  • 定义:软件要处理的业务问题范围和相关知识集合。
  • 理解:如电商领域、医疗领域、金融领域。它是所有设计的出发点。

2. 子域 (Subdomain)

  • 定义:领域的细分模块,一个领域由多个子域构成。
  • 分类(核心)
    • 核心域 (Core Domain):业务的核心竞争力,需重点投入、自主研发。(例:电商的交易系统
    • 支撑域 (Supporting Domain):支撑核心业务,但非差异化优势。(例:电商的物流管理
    • 通用域 (Generic Domain):通用功能,无业务特性,可外包或使用开源。(例:用户认证、日志

3. 限界上下文 (Bounded Context, BC)

  • 定义DDD 最重要的概念。模型语义生效的边界
  • 作用
    • 每个 BC 内有独立的通用语言独立的领域模型
    • 解决“一词多义”问题。例如:产品商品上下文是描述、价格;在物流上下文是重量、体积。
  • 关系:通常一个限界上下文对应一个微服务或一个开发团队。

4. 上下文映射 (Context Mapping)

  • 定义:描述限界上下文之间的协作关系与集成方式
  • 常见模式
    • 客户-供应商 (Customer-Supplier):下游依赖上游,上游主导。
    • 防腐层 (Anti-Corruption Layer, ACL):下游通过适配层隔离上游,保护自身模型纯净。
    • 共享内核 (Shared Kernel):两个团队共享一小部分模型(慎用,易耦合)。
    • 发布-订阅 (Publish-Subscribe):通过领域事件解耦通信。

5. 统一语言 (Ubiquitous Language)

  • 定义:团队共同创建的业务术语词典
  • 要求:术语必须一致地出现在代码、文档、日常对话中。
  • 价值:消除“业务说业务,开发做开发”的鸿沟。

三、战术设计核心名词(微观建模)

1. 实体 (Entity)

  • 定义:拥有唯一标识符(ID)属性可变但身份不变的对象。
  • 特征:有生命周期、有状态、需被追踪。
  • 示例用户(User)订单(Order)。ID 不变,订单状态可从待付款变为已完成。

2. 值对象 (Value Object)

  • 定义没有唯一ID,由属性值定义的对象。
  • 特征不可变、无生命周期、可替换
  • 示例地址(Address)金额(Money)颜色(Color)
  • 原则:描述实体的特征,但本身不具备独立存在意义。

3. 聚合 (Aggregate) & 聚合根 (Aggregate Root)

  • 定义:一组高度关联的实体与值对象的集合,是数据修改与持久化的基本单元
  • 聚合根 (AR):聚合的入口/管理者,是一个特殊的实体
  • 规则(核心)
    1. 外部只能通过聚合根访问聚合内部对象。
    2. 事务边界:一个事务只能修改一个聚合
    3. 一致性:聚合根负责维护内部数据的业务一致性
  • 示例订单(Order)是聚合根,包含订单项(OrderItem)收货地址(Address)。外部只能调用 Order 的方法,不能直接修改 OrderItem

4. 领域服务 (Domain Service)

  • 定义:封装不适合放在单个实体/值对象上无状态业务逻辑
  • 场景:跨多个实体、跨聚合的业务逻辑。
  • 示例支付服务(PaymentService)价格计算服务(PricingService)
  • 原则:只做纯业务逻辑,不处理IO、事务、权限。

5. 领域事件 (Domain Event)

  • 定义:领域中发生的有业务意义的状态变更
  • 作用解耦。一个聚合发布事件,其他聚合/上下文异步订阅处理。
  • 示例订单已支付(OrderPaidEvent) → 触发库存扣减物流发货
  • 优势:最终一致性,高扩展性。

6. 仓储 (Repository)

  • 定义聚合根的容器,提供持久化(增删改查)能力。
  • 作用隔离领域层与数据库。领域层只依赖 Repository 接口,具体实现(MySQL/Mongo)在基础设施层。
  • 原则:一个聚合根对应一个 Repository。

7. 工厂 (Factory)

  • 定义:封装复杂对象(聚合)的创建逻辑
  • 作用:隐藏创建细节,保证对象创建时即完整有效

四、DDD 标准分层架构

DDD 通过分层隔离关注点,领域层绝对独立,不依赖任何框架或技术细节。

1. 用户接口层 (Interfaces)

  • 职责:处理外部请求(HTTP/RPC)、参数校验、返回DTO。
  • 组件:Controller、Facade、DTO 转换器。

2. 应用层 (Application)

  • 职责非常薄只编排,不实现业务。
  • 工作:调用领域服务/聚合根、事务控制、权限校验、发送领域事件。
  • 组件:XXXApplicationService。

3. 领域层 (Domain) —— 核心层

  • 职责表达所有业务概念、规则、状态
  • 组件:Entity、VO、Aggregate、Domain Service、Domain Event、Repository 接口。
  • 原则纯业务,无任何技术代码(无Spring、无JDBC)。

4. 基础设施层 (Infrastructure)

  • 职责:提供技术支撑
  • 组件:Repository 实现(MyBatis/JPA)、消息队列、日志、缓存、第三方SDK。

五、总结:DDD 核心关系图

领域 (Domain)
  ↳ 子域 (Subdomain) [核心/支撑/通用]
    ↳ 限界上下文 (Bounded Context) [微服务]
       ↳ 统一语言 (Ubiquitous Language)
       ↳ 领域模型 (Domain Model)
          ↳ 聚合 (Aggregate)
             ↳ 聚合根 (Aggregate Root) [实体]
             ↳ 实体 (Entity)
             ↳ 值对象 (Value Object)
          ↳ 领域服务 (Domain Service)
          ↳ 领域事件 (Domain Event)
          ↳ 仓储接口 (Repository)
       ↳ 应用服务 (Application Service) [编排]
       ↳ 基础设施 (Infrastructure) [技术实现]

一句话记住 DDD战略划边界,战术建模型;领域为核心,统一语言贯穿始终


下面是「电商订单业务」完整的 DDD 实战流程。

事件风暴 → 领域划分 → 限界上下文 → 聚合设计 → 代码结构 → 完整业务流程


一、业务场景(先明确我们要做什么)

简化版电商流程:

  1. 用户创建订单(选择商品、收货地址、支付方式)
  2. 系统校验库存、价格、优惠券
  3. 订单提交成功 → 待支付
  4. 用户支付 → 订单变成已支付
  5. 触发发货、扣减库存、通知物流

我们就用这个流程做一次完整 DDD 建模


二、第一步:事件风暴(DDD 最核心的拆分方法)

事件风暴的目的只有一个:
把业务拆干净,找到领域、子域、限界上下文

1. 找出所有领域事件(Domain Events)

领域事件 = 业务上发生了有意义的事
我们先全部列出来:

  • OrderCreatedEvent(订单已创建)
  • OrderSubmittedEvent(订单已提交)
  • OrderPaidEvent(订单已支付)
  • OrderCancelledEvent(订单已取消)
  • StockDeductedEvent(库存已扣减)
  • ShipmentCreatedEvent(物流单已创建)

2. 找出触发事件的命令(Command)

  • CreateOrderCommand
  • SubmitOrderCommand
  • PayOrderCommand
  • CancelOrderCommand

3. 找出参与的实体(Entity)

  • User(用户)
  • Order(订单)
  • OrderItem(订单项)
  • Product(商品)
  • Inventory(库存)
  • Shipment(物流单)
  • Payment(支付单)

4. 聚合分组(关键一步)

根据业务内聚性分组:

聚合 1:订单聚合

  • Order(聚合根)
  • OrderItem
  • Address(值对象)
  • OrderAmount(值对象)

聚合 2:商品库存聚合

  • Inventory(聚合根)
  • Product

聚合 3:支付聚合

  • Payment(聚合根)

聚合 4:物流聚合

  • Shipment(聚合根)

三、第二步:战略设计——划分领域 & 限界上下文

1. 领域 Domain

电商业务领域

2. 子域 Subdomain

  • 核心域:交易订单域(竞争力核心)
  • 支撑域:商品域、库存域、物流域
  • 通用域:用户域、支付域(可接第三方)

3. 限界上下文 Bounded Context

最终拆分出 4 个 BC(对应微服务):

  1. 用户上下文(User BC)
  2. 订单上下文(Order BC) —— 核心
  3. 商品库存上下文(Product BC)
  4. 支付上下文(Payment BC)
  5. 物流上下文(Shipment BC)

一个限界上下文 ≈ 一个微服务
内部有自己独立的模型、语言、数据库


四、第三步:战术设计——订单聚合模型(最核心)

1. 订单聚合结构

Order(聚合根 · 实体)
 ├─ OrderId(ID)
 ├─ UserId
 ├─ Address(值对象)
 ├─ OrderStatus(枚举)
 ├─ totalAmount(值对象)
 ├─ List<OrderItem>(子实体)
 └─ 行为方法:submit()、pay()、cancel()…

OrderItem(子实体)
 ├─ productId
 ├─ price
 └─ count

Address(值对象)
OrderAmount(值对象)

2. 实体 vs 值对象 清晰区分

  • 实体:有 ID、会变、有生命周期
    Order、OrderItem、Payment
  • 值对象:无 ID、不可变、只描述
    Address、Money、Coordinate、OrderAmount

3. 聚合根的规则(非常重要)

  1. 外部只能访问聚合根,不能直接操作内部子实体
  2. 一个事务只修改一个聚合
  3. 聚合根保证业务不变性规则
    例:订单已支付不能再修改数量

五、第四步:DDD 四层架构 + 代码结构(可直接复制用)

标准 DDD 包结构(Java 风格,通用所有语言)

order/
├─ interfaces/        # 接口层:Controller、DTO
├─ application/        # 应用层:编排业务
├─ domain/            # 领域层(核心)
│   ├─ entity/        # 实体、聚合根
│   ├─ vo/            # 值对象
│   ├─ event/         # 领域事件
│   ├─ service/       # 领域服务
│   └─ repository/    # Repository 接口
└─ infrastructure/    # 基础设施层
    └─ repository/impl # Repository 实现(MyBatis/JPA)

六、第五步:代码实战(最关键的业务逻辑)

我直接写订单创建 → 提交 → 支付的真实 DDD 风格逻辑。

1. 领域层:订单聚合根(核心业务规则在这里)

// 聚合根
public class Order {
    private OrderId id;
    private UserId userId;
    private Address address;
    private OrderStatus status;
    private OrderAmount totalAmount;
    private List<OrderItem> items;

    // 订单提交
    public void submit() {
        if (this.status != OrderStatus.DRAFT) {
            throw new IllegalStateException("只能提交草稿订单");
        }
        this.status = OrderStatus.PENDING_PAY;
        // 发布领域事件
        DomainEventPublisher.publish(new OrderSubmittedEvent(id));
    }

    // 订单支付
    public void pay() {
        if (this.status != OrderStatus.PENDING_PAY) {
            throw new IllegalStateException("订单状态不正确");
        }
        this.status = OrderStatus.PAID;
        DomainEventPublisher.publish(new OrderPaidEvent(id, userId));
    }
}

重点:
所有业务规则、校验、状态变更都在领域类内部
不在 Service 里乱写

2. 领域事件(跨服务解耦神器)

public class OrderPaidEvent {
    private OrderId orderId;
    private UserId userId;
}

订单支付后,发布事件:

  • 库存服务监听 → 扣库存
  • 物流服务监听 → 创建物流单
  • 消息服务监听 → 发推送

完全解耦,高内聚低耦合

3. 领域服务(跨聚合逻辑)

public class OrderDomainService {

    // 创建订单,同时校验价格、库存
    public Order createOrder(User user, List<Product> products, Address address) {
        // 逻辑跨多个聚合,所以放在 DomainService
        Order order = OrderFactory.create(user, products, address);
        return order;
    }
}

4. 应用层(只编排,不写业务)

@Service
public class OrderApplicationService {

    private OrderRepository orderRepository;
    private OrderDomainService orderDomainService;

    // 创建订单
    @Transactional
    public OrderDto createOrder(CreateOrderCommand cmd) {
        User user = userRepository.get(cmd.getUserId());
        List<Product> products = productRepository.list(cmd.getProductIds());

        Order order = orderDomainService.createOrder(user, products, cmd.getAddress());
        orderRepository.save(order);

        return OrderConverter.toDto(order);
    }

    // 支付订单
    @Transactional
    public void payOrder(PayOrderCommand cmd) {
        Order order = orderRepository.get(cmd.getOrderId());
        order.pay();          // 领域行为
        orderRepository.save(order);
    }
}

5. 接口层(Controller)

@RestController
@RequestMapping("/order")
public class OrderController {

    private OrderApplicationService orderAppService;

    @PostMapping("/create")
    public Result<OrderDto> create(@RequestBody CreateOrderRequest req) {
        CreateOrderCommand cmd = CommandConverter.toCommand(req);
        return Result.success(orderAppService.createOrder(cmd));
    }
}

6. 仓储接口(领域只依赖接口,不依赖实现)

public interface OrderRepository {
    Order get(OrderId id);
    void save(Order order);
}

实现类放在 infrastructure 层,用 MyBatis / JPA 都行。


七、第六步:完整业务流程(DDD 视角跑一遍)

  1. 前端请求 → Controller
  2. Controller 转 Command → 应用服务
  3. 应用服务
    • 加载聚合
    • 调用聚合根方法(真正业务逻辑)
    • 保存聚合
    • 事务提交
  4. 聚合内部发布领域事件
  5. 其他微服务监听事件执行自己逻辑

整个系统做到:

  • 业务逻辑高度内聚
  • 状态安全可控
  • 扩展极强,加逻辑只加事件
  • 微服务之间高度解耦

八、最后给你一张 DDD 终极总结图(电商版)

电商 Domain
  ├─ 核心域:订单上下文
  │    └─ 聚合:Order(AR) → OrderItem、Address、Amount
  ├─ 支撑域:商品库存上下文
  ├─ 支撑域:物流上下文
  └─ 通用域:用户、支付上下文

领域模型内部
  聚合根 → 实体 → 值对象
  领域服务 → 处理跨聚合逻辑
  领域事件 → 跨服务通信

架构
  接口层 → 应用层 → 领域层 → 基础设施层

posted @ 2026-04-02 13:48  蓝迷梦  阅读(47)  评论(0)    收藏  举报