领域驱动设计:从“八股”到“实战”的思维跃迁
最近和几位资深架构师聊天,大家不约而同地提到了一个现象:很多团队在引入领域驱动设计(DDD)后,项目初期热情高涨,各种实体、值对象、聚合根画得满满当当,但半年后回头再看,代码库却陷入了一种奇怪的僵化状态——模型看似“标准”,却与业务现实渐行渐远,维护成本不降反升。这让我想起一个词:“DDD八股文”。
所谓“八股文”,在这里并非贬义,而是指一种现象:开发者熟练背诵DDD的术语(聚合根、限界上下文、领域服务),能在面试中对答如流,但在真实、复杂、动态的业务战场上,却无法将这些概念灵活地转化为可持续、可演进的软件设计。问题不在于DDD本身,而在于我们是否将其视为一套需要“填空”的固定模板,而非一种需要持续探索和对话的思维方式。
这篇文章,我想抛开那些教科书式的定义,结合我近几年在多个中大型业务系统重构中应用DDD的实战经验,聊聊如何避开那些让DDD实践流于形式的常见陷阱。我们关注的不是“应该”是什么,而是“在什么情况下”怎么做才有效,以及当理想模型撞上现实约束时,如何做出务实且不违背核心原则的取舍。
1. 误区一:建模始于“画图”,而非“对话”
很多团队启动DDD项目的第一步,是召集所有人到会议室,打开绘图工具,开始画领域模型图。实体、关系、属性,讨论得热火朝天。这看起来没错,但往往埋下了第一个隐患:我们是在为“已知”的业务建模,还是在探索“未知”的业务复杂性?
真正的领域建模,起点必须是事件风暴或类似的深度协作工作坊,但其核心目的不是产出那张完美的图,而是建立团队(包括业务专家、产品经理、开发、测试)之间的通用语言。我经历过一个支付清结算项目,初期我们花了大量时间争论“交易”实体应该包含二十几个还是三十几个字段。直到我们引入了一位真正的财务专家,她用了十分钟在白板上画出了资金流转的生命周期——从“支付指令接收”到“银行通道处理”,再到“清算批次生成”和“结算文件下发”。那一刻我们才发现,之前纠结的“交易”实体,实际上包含了至少三个具有不同生命周期和变更频率的概念:支付订单、清分记录、结算凭证。
提示:在事件风暴中,请业务专家用他们最自然的语言描述业务流程,重点记录“当...发生时”这样的事件语句。避免过早引入“实体”、“服务”等技术术语。
一个有效的通用语言,会自然体现在代码的命名中。例如,在上述项目里,我们最终的核心聚合根是 ClearingBatch(清算批次),而不是笼统的 Transaction。它的行为是 closeBatch()(关闭批次以生成结算文件),而不是 updateStatus()。当代码中的术语与业务人员口中的术语完全一致时,沟通成本和误解率会显著下降。
如何让对话更有效?
- 邀请真正的决策者:确保参与工作坊的业务方,是有权对业务规则做出解释和决策的人,而不是单纯的传声筒。
- 聚焦“问题域”而非“解决方案域”:多问“为什么会有这个规则?”、“这个异常情况发生的频率和影响是什么?”,而不是“这个功能我们该怎么实现?”。
- 使用便签纸等低精度工具:这能鼓励快速修改和重组想法,避免大家过早地执着于某个“精美”的图表。
2. 误区二:聚合设计沦为“数据库表”的翻版
这是最具迷惑性的陷阱。我们理解了“聚合是一组具有一致性边界的对象的集合,由聚合根统一对外提供访问”,但在设计时,却不自觉地被底层数据库的表结构牵着鼻子走。常见的反模式是:一个数据库表对应一个聚合,表的主键就是聚合根的ID,表的外键关系直接映射为聚合间的引用。
这样做带来的直接问题是聚合边界过大,事务范围失控。例如,在一个电商系统中,如果把“用户

662

被折叠的 条评论
为什么被折叠?



