DDD 不是“写代码的技巧”,而是复杂业务系统的建模方法论。很多人学 DDD 学废了,本质就是:没理解“领域”二字。
我用工程师能落地的方式给你把 DDD 的原理一次讲清楚。
一、DDD 解决的核心问题是什么?
一句话总结:
DDD 是为了解决「复杂业务系统失控」的问题
典型痛点你一定遇到过:
- Service 越写越胖(2000 行一个 Service)
- 需求一改,全系统联动
- if/else 地狱,状态组合爆炸
- 业务规则散落在 Controller / Service / Mapper
- 新人不敢改代码,老兵也怕改
这些问题本质不是技术问题,是“业务建模失败”。
DDD 的目标不是优雅代码,而是:
让代码结构 = 业务结构
二、DDD 的核心思想(三句话)
1️⃣ 领域(Domain)是第一公民
-
技术是为业务服务的
-
不是先想数据库、接口、表结构
-
而是先想:
- 业务规则是什么?
- 状态如何流转?
- 谁负责做什么?
👉 领域模型决定代码结构
2️⃣ 复杂度要被“限制在边界内”
没有边界的复杂系统,一定会腐烂
DDD 用 边界 来隔离复杂度:
- 不同业务用不同模型
- 不同上下文用不同语言
- 不允许“跨领域乱调”
👉 复杂度不可消除,但可以隔离
3️⃣ 代码要能“表达业务意图”
DDD 反对这种代码:
if (status == 1 && role == 3 && flag == true) {// ...
}
DDD 追求的是:
order.pay();
order.refund();
order.cancel();
👉 读代码就像读业务说明书
三、DDD 的四个核心构件(必须吃透)
这是 DDD 的“骨架”。
1️⃣ 实体(Entity)
特征:有身份(ID),会变化
例子:
- 用户 User
- 订单 Order
- 账户 Account
关键点:
- 实体 = 业务行为的载体
- 不只是数据结构
- 实体内部应该有业务逻辑
❌ 错误示例(贫血模型):
class Order {Long id;Integer status;
}
✅ 正确示例(充血模型):
class Order {void pay() { ... }void cancel() { ... }
}
2️⃣ 值对象(Value Object)
特征:无身份、不可变、用来表达概念
例子:
- Money
- Address
- Price
核心价值:
消灭“魔法字段”和重复校验
class Money {BigDecimal amount;String currency;
}
你立刻就知道:
这是“钱”,不是随便一个 BigDecimal。
3️⃣ 聚合 & 聚合根(DDD 最重要的概念)
聚合 = 一致性边界
- 一个聚合内,数据强一致
- 聚合之间,只能通过 ID 关联
- 外部只能操作 聚合根
例子:
Order(聚合根)├── OrderItem└── Address
规则:
- 不能直接操作 OrderItem
- 只能通过 Order 修改它
👉 这是防止业务规则被绕开的“护城河”
4️⃣ 领域服务(Domain Service)
当业务逻辑:
- 不适合放在某一个实体里
- 又属于领域规则
就用领域服务。
例如:
- 转账(涉及两个账户)
- 复杂定价策略
- 跨实体规则校验
⚠️ 注意:
不是所有 Service 都是领域服务
领域服务:
- 无状态
- 只表达领域规则
四、DDD 的“地图”:限界上下文(Bounded Context)
这是 DDD 能落地的关键点。
什么是限界上下文?
一句话:
在这个边界内,业务词汇的含义是唯一的
例子:
“订单”在不同系统里:
| 系统 | 订单含义 |
|---|---|
| 交易系统 | 用户下单 |
| 支付系统 | 支付单 |
| 履约系统 | 发货单 |
👉 不能强行复用一个 Order
每个上下文:
- 自己的模型
- 自己的表
- 自己的接口
五、DDD 的经典分层(工程视角)
这是你作为 Java 工程师最容易落地的部分
四层架构(不是三层 MVC)
接口层(Controller / API)
↓
应用层(Application Service)
↓
领域层(Domain)
↓
基础设施层(Repository / MQ / DB)
各层职责一句话版:
- 接口层:协议转换(HTTP / RPC)
- 应用层:用例编排(事务、调用顺序)
- 领域层:纯业务规则(DDD 的核心)
- 基础设施层:技术细节
👉 领域层不依赖任何技术框架
六、很多人 DDD 用失败的原因(实话)
我直说,不给你灌鸡汤:
- 一上来就全系统 DDD → 必死
- 把 DDD 当成目录规范 → 自欺欺人
- 实体没行为,全写在 Service → 伪 DDD
- 不画业务图,只画类图 → 南辕北辙
- 没有限界上下文 → 迟早崩盘
七、给你的务实建议(结合你 10 年 Java 经验)
你不需要“宗教式 DDD”,而是 “可控 DDD”:
第一阶段(最重要)
-
只做三件事:
- 聚合根
- 实体行为
- 限界上下文
第二阶段
- 引入值对象
- 拆分应用层 / 领域层
第三阶段
- 事件驱动
- CQRS(选用,不是必须)