Skip to content

DDD 架构 —— 领域驱动设计 ​

DDD(Domain-Driven Design,领域驱动设计)不是某个框架,而是一套代码组织方法论:以业务领域为中心建模和组织代码,而不是按技术职能分层。它和微服务是绝配——微服务回答"拆几个服务",DDD 回答"边界怎么划"。

一、为什么会有 DDD:三层架构的痛点 ​

传统三层架构(Controller → Service → Dao)按技术职能分层,小项目很清晰,但业务复杂后会变异:

java
// 一个几千行的 OrderService,塞满所有和订单有关的逻辑
public class OrderServiceImpl {
    // 下单、取消、退款、改地址、查询、分页、导出……
    // 全是过程式代码:先查库 → 一堆 if/else → 再 update
}

痛点:

  1. 贫血模型:实体类(Entity/PO)只有 getter/setter,不含任何业务逻辑,逻辑全堆在 Service,业务规则散落难找
  2. 面向数据库建模:先设计表结构,代码围着表转,业务语义被稀释成一条条 SQL
  3. 改一个业务要横跨所有层:Controller、Service、Dao、DTO 全摸一遍
  4. 沟通鸿沟:产品说"库存扣减",开发脑中是 update stock set num = num - 1,同一个业务两套语言

DDD 的主张:业务逻辑归还给业务对象(充血模型),代码结构跟着业务边界走。

二、DDD 核心概念速览 ​

战略设计(宏观:怎么划边界) ​

概念含义
领域 / 子域整个业务系统;按价值拆成核心域(赚钱的,如订单)、支撑域(必须但不核心,如库存)、通用域(谁都要,如登录鉴权)
限界上下文一个模型成立的边界——同一个"商品"在售前上下文叫 SKU,在物流上下文叫包裹。微服务拆分的最佳单位就是限界上下文
通用语言业务和开发说同一种话:代码里的类名、方法名直接用业务术语(Order.cancel() 而不是 updateStatus(3))

战术设计(微观:领域层怎么写) ​

概念含义例子
实体 Entity有唯一标识、有生命周期的对象订单(靠 orderId 识别)
值对象 Value Object无标识、不可变,靠属性判等地址、金额
聚合 Aggregate一致性边界,一个事务只改一个聚合订单+订单明细 = 订单聚合
聚合根聚合的入口,外界只能通过它操作聚合内部Order 是根,不能绕过它直接改 OrderItem
仓储 Repository持久化的抽象,只给聚合根建仓储orderRepository.save(order)
领域事件领域内发生的事,用于解耦OrderCancelledEvent

三、代码对比:贫血 vs 充血(DDD 最直观的差异) ​

传统三层(贫血)——逻辑在 Service:

java
public void cancelOrder(Long orderId) {
    Order order = orderMapper.selectById(orderId);
    if (order.getStatus() != 2) {              // 魔法数字,规则散落各处
            throw new BizException("已支付订单才能取消");
    }
    order.setStatus(3);
    orderMapper.updateById(order);
}

DDD(充血)——规则内聚在领域对象:

java
public class Order {                // 领域对象自己管自己的规则
    public void cancel() {
        if (this.status != Status.PAID) {
            throw new BizException("已支付订单才能取消");
        }
        this.status = Status.CANCELLED;
        this.events.add(new OrderCancelledEvent(this.id));  // 领域事件
    }
}

// Service 退化为"用例编排":取聚合 → 调领域方法 → 存仓储 → 发事件
public void cancelOrder(Long orderId) {
    Order order = orderRepository.findById(orderId);
    order.cancel();                              // 规则在领域对象里
    orderRepository.save(order);
}

好处:业务规则只有一处,且用业务语言写的,产品经理几乎能读懂。

四、DDD 分层架构(对比三层) ​

传统三层(依赖方向朝下,最终围着数据库转):
Controller → Service → Dao → 数据库

DDD 四层(领域层是核心,依赖倒置,基础设施依赖领域):
┌─────────────────────────────┐
│ 用户接口层 interfaces   │ Controller、对外 DTO
│ 应用层 application           │ 用例编排、事务,不含业务规则
│ 领域层 domain ⭐             │ 实体、聚合、领域事件、领域服务(核心)
│ 基础设施层 infrastructure    │ Repository 实现、MQ、缓存
└─────────────────────────────┘

关键区别:

  • 三层的核心是 Dao/数据库,DDD 的核心是领域层
  • 依赖倒置:domain 定义 OrderRepository 接口,infrastructure 提供实现(MyBatis/JPA)——领域层不依赖任何技术细节,换数据库领域层一行不改

五、横向对比:DDD vs 其他架构 ​

先分清两个正交的维度,很多混淆来自把两者混着比:

  • 部署架构(单体 / 微服务):回答"拆几个独立部署的服务"
  • 设计方法论(三层 / DDD / 整洁架构):回答"服务内部代码怎么组织"
架构类型一句话和 DDD 的关系
MVC/三层设计方法论按技术职能分层DDD 的对照面;CRUD 项目用三层更好
微服务部署架构按业务拆独立部署的服务正交互补:用 DDD 划限界上下文 → 一个上下文落地成一个微服务
整洁/六边形架构设计方法论依赖指向内,业务核心不依赖框架/DB和 DDD 一脉相承,DDD 的分层通常用整洁/六边形实现
事件驱动 EDA通信架构服务/模块间通过事件异步协作领域事件天然适配 EDA,跨上下文解耦常用 MQ 事件

常见搭配:单体 + DDD(不拆服务但代码按上下文分包)、微服务 + DDD(最主流的大厂组合)。反过来"微服务但每个服务里还是三层贫血"也大量存在——拆了服务但没拆明白。

六、什么时候用 DDD ​

适合:

  • 业务规则复杂、多变的系统(金融、电商交易、ERP、SaaS 核心域)
  • 系统要长期演进,生命周期以年计
  • 正要做微服务,需要划分服务边界的方法论
  • 团队和产品沟通成本高,需要通用语言对齐认知

不适合:

  • CRUD 管理系统、外包项目、小工具——三层 + SQL 三天搞定,上 DDD 纯属自虐
  • 团队没人懂 DDD 就强行上——概念多、上手陡,容易写出"四不像"(分层齐全但依然贫血)

⚠️ DDD 的成本是真实存在的:概念量、代码量、抽象层级都上升。正确姿势是只给核心域上 DDD,支撑域和通用域继续用简单三层。

七、落地与学习建议 ​

  1. 先从充血模型 + 一个聚合练手:拿自己项目里最复杂的一个业务(如订单),把规则从 Service 挪进领域对象,体会内聚
  2. 再练限界上下文:画业务流程图,找"同一个词在不同环节含义不同"的地方,那就是上下文边界
  3. 微服务落地:一个限界上下文 = 一个服务 = 一套独立数据库
  4. 书:入门看《解构领域驱动设计》(张逸)或《实现领域驱动设计》(红皮书),原典《领域驱动设计》(蓝皮书)偏理论,别第一本就读它

知识是财富,分享是快乐!