DDD
DDD领域驱动设计完整知识总结
DDD(Domain‑Driven Design)领域驱动设计,是一套针对复杂业务系统的建模方法论,不是框架。
核心思想:把业务领域作为软件系统的核心,数据库、框架只是外围技术;分为战略DDD(划分业务模型边界)与战术DDD(对象建模);常搭配洋葱/六边形架构落地。
适用场景:业务规则复杂、长期迭代的核心业务;简单CRUD系统不建议强行使用DDD,避免过度设计。
一、DDD核心基础概念
- 软件的核心是业务领域,而不是数据库、框架。
- 先做战略设计(划分边界),再做战术建模(写对象代码)。
- 业务规则尽量内聚到领域对象,不要散落在业务Service。
- 依赖倒置DIP:领域层(业务内核)不依赖任何外部框架,外部技术适配领域。
- 业务不变量:业务上必须永远成立的约束,由聚合根保证。
二、战略DDD:划分业务边界
战略DDD解决:系统怎么切分,模型边界在哪里;很多人忽略这一步直接写代码,导致DDD落地失败。
2.1 领域、子域
- 领域 Domain:整个软件覆盖的业务范围。
- 子域 Subdomain(业务视角),把大领域拆分成若干业务子域
- 核心子域:企业核心价值业务,重点投入资源建模。例:电商订单。
- 支撑子域:保障业务运行,不构成核心竞争力。例:优惠券。
- 通用子域:通用能力,优先复用现成组件。例:消息通知、文件存储。
2.2 限界上下文 Bounded Context
限界上下文是模型边界,不是模块、不是包、不是微服务。
在一个限界上下文内部,业务词汇含义统一;出了边界,相同名词可以拥有完全不同含义。
举例:
- 会员BC中的"用户":userId、手机号、会员等级、积分、地址列表(实体,可编辑)
- 订单BC中的"用户":仅userId、收件人、收货快照地址,不需要会员等级积分。
如果没有限界上下文,会试图维护一个全局大模型对象,字段无限膨胀,耦合严重。
上下文之间禁止直接共享领域实体对象。交互两种方式:
- RPC接口调用,配合防腐层ACL;
- 发布/订阅领域事件。
2.3 通用语言
产品、开发、测试使用同一套业务词汇;词汇直接映射类名、方法名。
限界上下文内部通用语言生效;不同上下文,词汇语义可以不一样。
2.4 防腐层 ACL
Anti‑Corruption Layer,防腐层,是一种跨边界的隔离适配机制,实现通常放在基础设施层(interfaces层对接外部API时也可能需要)。
外部上下文返回的数据模型,不能直接在本上下文使用;通过防腐层做模型转换,把外部DTO转为我方领域对象,防止外部系统模型变更污染本上下文领域模型。
2.5 子域 / 限界上下文 / 微服务三者关系
- 子域:业务能力视角
- 限界上下文:业务模型边界视角
- 微服务:部署物理视角
注意对应关系:一个子域可能对应多个限界上下文(大子域拆分),一个限界上下文通常只对应一个子域。禁止简单粗暴地认为"1子域 = 1限界上下文 = 1微服务"。
落地:初期多个简单限界上下文放在同一个服务(不同包);业务膨胀后,再拆分微服务。先逻辑边界,后物理拆分。
三、战术DDD:限界上下文内部对象建模
在一个限界上下文内部,对业务对象建模。
3.1 实体 Entity
- 拥有全局唯一ID;有生命周期,状态可变;相等判断依据ID。
- 可以包含业务行为(充血模型)。
示例:订单Order、商品SKU。
3.2 值对象 Value Object
- 没有独立ID;由一组属性构成;不可变;相等判断对比全部属性。
- 没有独立的生命周期标识,通常用来描述实体的属性特征。
示例:金额OrderMoney、收货地址ShippingAddress。
不可变:修改时返回新的值对象实例,不修改原有对象。
3.3 聚合 & 聚合根 Aggregate & AggregateRoot
- 聚合:一组强相关对象,是业务一致性边界;定义事务边界。
- 聚合根 AR:聚合对外的唯一访问入口。外部只能通过聚合根访问聚合内部子对象。
- 子实体不能对外暴露,不能有独立Repository。
重要约束:一个事务只修改一个聚合根。
示例:订单聚合
- 聚合根:Order
- 子实体:OrderItem订单项;外部不能直接操作订单项,只能通过Order操作。
- 值对象:OrderMoney、ShippingAddress。
- 业务不变量:订单总金额=所有订单项金额之和,聚合根内部保证。
3.4 领域服务 Domain Service
存放业务规则,位于
domain/service;无DB、无MQ、无外部接口调用。
适用场景(满足其一就抽领域服务):
- 业务行为跨多个聚合根,不属于任何一个聚合;
- 业务算法/计算逻辑,无法归属到某一个实体。
只做业务计算与校验,返回计算结果;不做持久化,持久化交给上层应用服务。
3.5 应用服务 Application Service
位于
application层;不包含核心业务规则。
职责:
- 用例流程编排;
- 获取聚合根对象;
- 事务控制;
- 调用仓储保存聚合;
- 拉取、发布领域事件。
区分记忆:
- 领域服务:管业务规则、计算(domain层)
- 应用服务:管流程、事务、存储(application层)
3.6 仓储 Repository
DIP依赖倒置落地关键:
- Repository接口定义在domain领域层;
- Repository实现在infrastructure基础设施层。
- Repository面向聚合根;只保存/查询聚合根。子实体不定义Repository。
3.7 领域事件 Domain Event
- 表达已经发生的业务事实;命名使用过去式,例如
OrderCreatedEvent。 - 在聚合根内部产生,存放事件列表;
- 保存聚合之后,由应用层拉取并发布事件;
- 用于解耦业务流程;可以MQ异步消费。
四、DDD + 洋葱架构包结构
com.ecommerce.order // 订单限界上下文
├── domain // 领域层【最内层,不能引入框架】
│ ├── entity // 实体、聚合根、子实体
│ ├── vo // 值对象
│ ├── event // 领域事件
│ ├── repository // Repository【接口】
│ └── service // 领域服务
├── application // 应用层,流程编排
│ ├── dto // Command/DTO
│ └── service // 应用服务
├── infrastructure // 基础设施层
│ ├── repository // Repository实现类
│ ├── acl // 防腐层ACL,对接外部上下文
│ └── message // MQ事件发布消费
└── interfaces // 接口层 Controller/RPC
└── controller
强制约束:domain包下禁止import Spring、MyBatis等框架类。
五、实战案例:多订单优惠券分摊(领域服务示例)
业务场景:一张优惠券,分摊抵扣多个订单金额。
逻辑跨多个Order聚合根,不属于单个Order,放入领域服务。
领域服务(domain层,仅计算,不持久化)
// domain/service/OrderDiscountDomainService
public class OrderDiscountDomainService {
public List<DiscountAllocationResult> allocateDiscount(
List<Order> orders,
CouponDiscount couponDiscount
) {
// 1.业务规则校验
// 2.比例分摊计算,处理小数精度
// 3.返回分摊结果值对象
}
}
应用服务调用
@Service
public class OrderApplicationService {
@Transactional
public void applyCoupon(Long couponId, List<Long> orderIdList){
//1.查询多个订单聚合根
//2.构建优惠券值对象
//3.调用领域服务做分摊计算
//4.使用返回结果修改订单聚合根状态
//5.保存聚合根、发布领域事件
}
}
领域服务负责业务计算;应用服务负责流程、事务、存储。
六、充血模型 vs 贫血模型
- 贫血模型(反模式)
领域实体只有getter/setter;所有业务校验、状态逻辑全部写在应用服务。
业务规则散落在Service,领域对象变成单纯数据载体。
- 充血模型(DDD提倡)
业务校验、状态变更、业务不变量逻辑下沉到聚合根/实体内部;
应用服务只做编排。
示例:
order.pay()、order.cancel()业务方法定义在Order聚合根内部。
七、DDD常见踩坑清单
- 跳过战略DDD,直接写实体聚合;限界上下文划分缺失。
- Repository接口写在基础设施层,违反DIP依赖倒置。
- 子实体单独创建Repository,绕过聚合根操作子对象。
- 领域层引入Spring、Mybatis框架依赖。
- 贫血模型:实体只有get/set,业务逻辑全部堆在应用服务。
- 聚合设计过大,聚合内部塞进大量无关对象;一个事务修改多个聚合根。
- 限界上下文直接等同于微服务,上来就拆分微服务。
- 简单CRUD业务强行套用DDD全套概念,过度设计。
- 领域服务写数据库操作、调用外部接口。领域服务只做业务计算。
八、DDD落地判断:什么时候用,什么时候不用
适合DDD:
- 业务规则复杂多变;核心业务;长期迭代维护;大量业务概念。
不适合DDD:
- 简单CRUD系统;报表;一次性原型;业务逻辑单薄。
DDD是有成本的建模工具,不要为炫技而使用。

浙公网安备 33010602011771号