DDD-030:DDD 落地常见问题与避坑指南本章导读DDD 是一套强大的方法论,但在实际落地过程中往往会遇到各种挑战和陷阱。本章将系统性地总结 DDD 落地过程中的常见问题、错误做法和解决方案,帮助团队避免重蹈覆辙,提高 DDD 实施的成功率。学习目标识别 DDD 落地过程中的常见陷阱和错误理解 DDD 的适用场景和边界掌握成功落地的关键因素前置知识DDD 战略设计和战术设计完整知识实际项目开发经验阅读时长约 40-50 分钟【原理】DDD 落地常见问题一、过度设计的陷阱1.1 常见表现【历史架构问题】// ❌ 过度设计示例 1:简单 CRUD 也用 DDD// 只是一个简单的配置管理,也设计了完整的聚合publicclassSystemConfigextendsAggregateRootConfigId{// 只有一个 key-value 对,却用了聚合根// 完全没必要!}// ❌ 过度设计示例 2:所有实体都设计成聚合// 用户地址本来只是值对象,却设计成了聚合publicclassAddressextendsAggregateRootAddressId{privateStringprovince;privateStringcity;privateStringdetail;// 地址应该是值对象,不需要独立生命周期}// ❌ 过度设计示例 3:过度抽象// 为了"灵活性",设计了过多的抽象层publicinterfaceRepositoryT,ID{}publicinterfaceOrderRepositoryextendsRepositoryOrder,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;}}// 只有复杂业务才使用 DDDpublicclassOrderextendsAggregateRootOrderId{// 订单有复杂的状态机和业务规则// 适合使用 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
DDD-030:DDD 落地常见问题与避坑指南
DDD-030:DDD 落地常见问题与避坑指南本章导读DDD 是一套强大的方法论,但在实际落地过程中往往会遇到各种挑战和陷阱。本章将系统性地总结 DDD 落地过程中的常见问题、错误做法和解决方案,帮助团队避免重蹈覆辙,提高 DDD 实施的成功率。学习目标识别 DDD 落地过程中的常见陷阱和错误理解 DDD 的适用场景和边界掌握成功落地的关键因素前置知识DDD 战略设计和战术设计完整知识实际项目开发经验阅读时长约 40-50 分钟【原理】DDD 落地常见问题一、过度设计的陷阱1.1 常见表现【历史架构问题】// ❌ 过度设计示例 1:简单 CRUD 也用 DDD// 只是一个简单的配置管理,也设计了完整的聚合publicclassSystemConfigextendsAggregateRootConfigId{// 只有一个 key-value 对,却用了聚合根// 完全没必要!}// ❌ 过度设计示例 2:所有实体都设计成聚合// 用户地址本来只是值对象,却设计成了聚合publicclassAddressextendsAggregateRootAddressId{privateStringprovince;privateStringcity;privateStringdetail;// 地址应该是值对象,不需要独立生命周期}// ❌ 过度设计示例 3:过度抽象// 为了"灵活性",设计了过多的抽象层publicinterfaceRepositoryT,ID{}publicinterfaceOrderRepositoryextendsRepositoryOrder,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;}}// 只有复杂业务才使用 DDDpublicclassOrderextendsAggregateRootOrderId{// 订单有复杂的状态机和业务规则// 适合使用 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