在架构设计领域,DDD(领域驱动设计)被讨论得最多,也被误解得最深。很多公司所谓的"官方指导文件"其实是在南辕北辙。如果不纠正这些根源上的错误,所谓的架构优化只能是空中楼阁。
以下是对当前行业内、甚至是某些大厂指导文件中典型错误做法的深度批判。
一、 批判:指导文件建议"不使用领域服务"
【现状批判】 :很多公司迷信"绝对充血模型",要求逻辑必须全写在领域实体中。然而,现实中大量业务涉及同领域内多个实体的对比与关联校验 (并非跨领域协作)。因为逻辑在单个实体里塞不进去,而指导文件又禁用了领域服务,程序员被迫将核心业务规则"偷渡"到应用层。
【正确方式】 :领域服务是逻辑的指挥官。 凡是涉及本领域内多实体协作、业务主键查重、规则验证,必须由领域服务承载。它是领域层对外的唯一逻辑入口,守住领域核心逻辑。
二、 批判:在应用层加入"公共方法目录"
【现状批判】 :由于领域逻辑被驱逐到了应用层,为了复用,开发者只能在应用层搞一个 Common 或 Util。这本质上是由于领域逻辑无法归位而导致的"架构流产"。
【正确方式】 :业务逻辑必须回归领域。 任何涉及规则的复用,必须沉淀在各自的领域服务中。应用层只负责调度流程,绝不负责存储业务规则。
三、 批判:指导文件明确"不使用聚合"
【现状批判】:有些指导文档主张平铺实体。没有聚合,实体就像散沙,应用层可以随意绕过业务规则修改实体状态,导致一致性防线全面崩溃。
【正确方式】 :每一个领域必须是一个聚合。 聚合根是唯一的入口。只有通过聚合根,才能保证业务规则(不变量)在任何时候都是有效的。聚合不是负担,而是保护业务逻辑的盔甲。
四、 批判:在应用层定义外部调用防腐接口
【现状批判】 :文档建议在应用层定义防腐接口,这会导致基础服务反向依赖应用层,造成严重的依赖环和逻辑污染。
【正确方式】 :新建领域对象,在领域层定义接口。 应该在领域层创建接口契约,由基础架构层(Infrastructure)去实现细节。最后,将该能力封装成领域服务供应用层调用。确保领域层自持,基础架构层只负责"插件式"实现。
五、 批判:领域服务调用其它领域的仓储
【现状批判】:在订单领域直接注入用户领域的 Repository。这种"跨界伸手段"让领域边界瞬间消失,两个领域在物理存储层面死死捆绑,未来根本无法拆分。
【正确方式】 :领域间绝对零感知。 领域服务只能调用本领域的仓储。跨领域的交互必须上浮到应用层,由应用层担任"导演"进行编排。
六、 批判:把领域对象用来接收领域事件
【现状批判】 :让实体或领域对象去监听 MQ 消息。这不仅引入了技术噪音,最致命的是让领域之间形成了依赖。一旦领域 A 的对象去监听领域 B 的事件,领域 A 就不再孤立,它被迫感知了外部世界的变化,破坏了领域自治。
【正确方式】 :应用层监听,领域服务处理。 应用层负责接收外部事件并将其"翻译"为本领域能理解的普通请求。领域对象应保持清静,它不该知道"事件"是从哪来的,更不该知道外部领域的存在。
领域对象设计原则:全业务封装与绝对隔离
-
领域必须封装本领域的所有业务 :通过领域服务实现业务逻辑的完全闭环。领域外层(应用层)不需要知道领域内部是如何判决的。
-
领域之间必须绝对隔离:领域之间要达到"物理级"的互不感知。它们不知道彼此的存在,更不能直接通信。
-
应用层是唯一的导演:只有应用层才有资格编排各个领域。应用层指挥 A 领域判决、B 领域执行,而领域本身只专注于自己。
总结:正确 DDD 的标准化原则
-
1-1-N 模组结构 :1 个仓储接口、1 个领域服务、N 个实体(构成 1 个聚合)。
-
契约自持:接口定义在领域层,拒绝反向依赖。
-
物理级隔离:领域间零感知,逻辑全封装,由应用层统一编排。
-
首错即断(Fail-fast):领域内遇错直接抛出业务异常。
只有正确的 DDD,才能正确的"微"服务。
微服务不是拆出来的,而是领域边界清晰后自然"长"出来的。
博主结语: 架构的优雅来自于对领域边界的极端克制。那些要求你"禁用领域服务"、"平铺实体"的指导文件,其实是在毁掉你的架构。拒绝虚伪的 DDD,守住领域层的尊严。