DDD-030:DDD 落地常见问题与避坑指南
本章导读
DDD 是一套强大的方法论,但在实际落地过程中往往会遇到各种挑战和陷阱。本章将系统性地总结 DDD 落地过程中的常见问题、错误做法和解决方案,帮助团队避免重蹈覆辙,提高 DDD 实施的成功率。
学习目标
- 识别 DDD 落地过程中的常见陷阱和错误
- 理解 DDD 的适用场景和边界
- 掌握成功落地的关键因素
前置知识
- DDD 战略设计和战术设计完整知识
- 实际项目开发经验
阅读时长
约 40-50 分钟
【原理】DDD 落地常见问题
一、过度设计的陷阱
1.1 常见表现
【历史架构问题】
// ❌ 过度设计示例 1:简单 CRUD 也用 DDD// 只是一个简单的配置管理,也设计了完整的聚合publicclassSystemConfigextendsAggregateRoot<ConfigId>{// 只有一个 key-value 对,却用了聚合根// 完全没必要!}// ❌ 过度设计示例 2:所有实体都设计成聚合// 用户地址本来只是值对象,却设计成了聚合publicclassAddressextendsAggregateRoot<AddressId>{privateStringprovince;privateStringcity;privateStringdetail;// 地址应该是值对象,不需要独立生命周期}// ❌ 过度设计示例 3:过度抽象// 为了"灵活性",设计了过多的抽象层publicinterfaceRepository<T,ID>{}publicinterfaceOrderRepositoryextendsRepository<Order,OrderId>{}publicinterfaceJpaOrderRepositoryextendsOrderRepository{}publicinterfaceCachedOrderRepositoryextendsOrderRepository{}// 实际上只需要一个 OrderRepository 就够了1.2 正确做法
// ✅ 根据复杂度选择合适的设计// 简单 CRUD:不需要 DDD@RestControllerpublicclassSystemConfigController{@AutowiredprivateSystemConfigRepositoryrepository;@GetMapping("/configs/{key}")publicStringgetConfig(@PathVariableStringkey){returnrepository.getValue(key);}@PutMapping("/configs/{key}")publicvoidsetConfig(@PathVariableStringkey,@RequestBodyStringvalue){repository.setValue(key,value);}}// 地址:使用值对象publicrecordAddress(Stringprovince,Stringcity,Stringdistrict,Stringdetail,StringpostalCode){publicStringgetFullAddress(){returnprovince+city+district+detail;}}// 只有复杂业务才使用 DDDpublicclassOrderextendsAggregateRoot<OrderId>{// 订单有复杂的状态机和业务规则// 适合使用 DDD}设计决策指南:
| 业务复杂度 | 推荐架构 | 说明 |
|---|---|---|
| 简单 CRUD | 传统三层 | 无复杂业务规则,不值得用 DDD |
| 中等复杂度 | 领域模型 | 提取核心领域对象,封装业务逻辑 |
| 高复杂度 | 完整 DDD | 聚合、领域事件、限界上下文全套 |
二、贫血领域模型
2.1 问题表现
// ❌ 典型的贫血模型@EntitypublicclassOrder{@IdprivateStringid;privateStringcustomerId;privateStringstatus;privateBigDecimaltotalAmount;// 只有 getter/setter,没有业务行为publicStringgetId(){returnid;}publicvoidsetId(Stringid){this.id=id;}publicStringgetStatus(){returnstatus;}publicvoidsetStatus(Stringstatus){this.status=status;}}// 业务逻辑都在 Service 层@ServicepublicclassOrderService{publicvoidpayOrder(String