关于 ddd-deeper-insight
面向已有 DDD 经验的团队,帮助持续深化领域模型。通过显式命名隐含概念、设计表达意图的接口及编码不变量,推动模型贴合领域认知。
主要功能
- 01识别并命名隐含领域概念
- 02设计表达意图的接口
- 03用断言编码模型不变量
- 04依据领域概念拆分职责
- 05运用纯函数与不可变值对象
- 06减少类的依赖并促进操作组合
适用场景
- 01改进虽能运行但难以理解的模型
- 02在持续建模中完善领域语言
- 03审视类职责过宽或依赖过多的设计
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yonatankarp/ software-design-skills --skill ddd-deeper-insight安装后在 Claude Code 中输入/ddd-deeper-insight确认已加载。
详细介绍
一句话概述
迈过第一个模型后,更深刻的洞见来自为模型隐藏的概念命名,并设计能揭示意图、防止误用的接口。
何时使用此技能
- 模型“能用”,但感觉别扭——代码需要靠注释来解释自己,方法接受的参数太多,名称总是差那么一点。
- 持续进行建模,语言每周都在逐渐精炼。
- 团队使用 DDD 已有一段时间,希望超越显而易见的模式,迈向灵活的设计。
何时不要使用此技能
- 从零开始进行初步设计 → 使用
ddd-design。 - 气味明确且有已知修复方法 → 直接使用
ddd-review和ddd-refactor。
核心内容
对 Evans 第 III 部分技巧的提炼。在模型需要时一次应用一项——这些技巧都不是强制要求。
突破是渐进的。 模型的第一个版本很少是最好的版本。即使它“能用”,也要继续建模。团队的理解会不断提升;模型也应随之改进。
让隐含概念显式化。 当规则隐藏在代码注释或零散的 if 语句中时,就将其命名为领域概念:Policy、Specification、Rate、EligibilityRule。名称本身就是通用语言的一部分。反过来说:如果注释在解释代码“真正的含义”,那么缺失的名称就是注释所描述的内容。
揭示意图的接口。 方法应根据其用领域术语实现的目标命名,而不是根据它的实现方式命名。Account.withdraw(amount) 揭示了意图;Account.modifyBalance(-amount) 描述的是实现机制。
无副作用函数。 尽可能将逻辑放入纯函数中,由函数返回新的值对象,而不是修改状态。模型中不修改状态的部分最容易推理,也最容易组合。
断言。 在代码中编码不变量——构造函数的前置条件、方法的后置条件、实体状态的不变量——而不只是写在文档中。如果不变量只存在于文字描述中,它们就会逐渐偏离实际情况。
概念轮廓。 按领域概念的自然边界拆分类,而不是为了技术上的便利。如果两部分行为因不同原因而变化,或演进速度不同,就应该放在不同的位置。
独立类。 尽量减少类对外部的依赖。耦合是一种建模异味——高度耦合的类通常包含多个尚未由模型区分开的概念。
运算封闭性。 输入和输出类型相同的运算可以顺畅组合。Money.add(Money) → Money 可以组合;Money.add(Amount) → BigDecimal 则不行。封闭性让模型具有代数性——运算可以自然地串联起来。
决策启发
- 如果注释在解释代码“真正的含义”,那么注释所命名的概念很可能尚未纳入模型。
- 纯函数和不可变值对象让模型更易于推理;默认优先采用它们,只有在实体的生命周期需要时才接受可变性。
- 耦合是建模异味,不只是代码质量问题——如果一个类依赖许多其他类,说明它承担的建模职责过于宽泛。
- 不要为了模式而重构。只有在领域需要时才应用这些技巧,而不是因为书中列出了它们。
反模式
- 只为“听起来像 DDD”而重命名类,却不改变结构或职责。
- 只因为书中提到了
Policy或Specification类就引入它们,而不是因为领域需要它们。 - “运算封闭性”表演——强行让运算共用类型,即使它们本来就不适合共用。
- 没有明确的目标状态就进行重构——灵活的设计是一种方向,而不是一份检查清单。
另请参阅
ddd-refactor— 操作层面的对应技能;采用一系列小步骤。ddd-value-object— 运算封闭性和不可变性在这里最能发挥作用。ddd-ubiquitous-language— 为隐含概念命名,本身就是在丰富语言。
参考资料
- Evans,《领域驱动设计》(2003 年),第三部分,第 8–13 章(突破、使隐含概念显式化、柔性设计、分析模式、设计模式、重构以获得更深刻的洞见)。