mcpField
M
moai-workflow-ddd

moai-workflow-ddd

github.com/modu-ai开发1.2k98 次安装今天更新
代码仓库

关于 moai-workflow-ddd

moai-workflow-ddd 是用于对现有代码库进行行为保持型重构的工作流,尤其适合测试覆盖有限的项目。它引导团队分析代码、编写特征测试,并在持续验证中逐步改进结构。

主要功能

  1. 01分析领域边界与耦合
  2. 02用特征测试记录现有行为
  3. 03将快照用作行为契约
  4. 04通过测试检查逐步重构
  5. 05使用 AST-grep 分析和转换代码

适用场景

  1. 01在不改变行为的前提下重构遗留代码
  2. 02减少生产系统的技术债
  3. 03在保留契约的同时迁移 API
  4. 04借助测试安全网改进代码结构

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/modu-ai/moai-adk --skill moai-workflow-ddd

安装后在 Claude Code 中输入/moai-workflow-ddd确认已加载。

详细介绍

领域驱动开发(DDD)工作流

开发模式配置(重要)

[备注] 此工作流根据 .moai/config/sections/quality.yaml 进行选择:

constitution:
  development_mode: ddd    # or tdd

何时使用此工作流:

  • development_mode: ddd → 使用 DDD(此工作流)
  • development_mode: tdd → 改用 TDD(moai-workflow-tdd)

主要区别:

  • DDD:针对测试覆盖率较低的现有代码库,优先编写特征测试
  • TDD(默认):所有工作均采用测试优先开发,包括在预先进行 RED 分析的棕地项目

快速参考

领域驱动开发为重构现有代码库提供了一种系统化方法,尤其适用于必须保留行为的场景。与用于创建新功能的 TDD 不同,DDD 改进结构而不改变行为。

核心周期——分析-保留-改进:

  • 分析:识别领域边界、耦合度量、AST 结构分析
  • 保留:特征测试、行为快照、验证测试安全网
  • 改进:逐步进行结构变更,并持续验证行为

何时使用 DDD:

  • 重构已有测试的遗留代码
  • 改进代码结构而不改变功能
  • 减少生产系统中的技术债务
  • 处理 API 迁移和弃用
  • 代码现代化项目
  • 绿地项目(采用调整后的周期——见下文)

何时不使用 DDD:

  • 需要改变行为时(先修改 SPEC)
  • 代码已存在,且目标是改变行为而非进行保留行为的重构时(DDD 会保留行为;如需实现新行为,请先修改 SPEC,或使用 TDD)

绿地项目适配:

对于没有现有代码的新项目,DDD 会调整其周期:

  • 分析:使用需求分析代替代码分析
  • 保留:通过规范测试定义预期行为(测试优先)
  • 改进:实现代码以满足已定义的测试

这使 DDD 成为 TDD 的超集——它既包含 TDD 的测试优先方法,也支持重构场景。


核心理念

DDD 与 TDD 对比

TDD 方法(适用于新功能):

  • 周期:RED-GREEN-REFACTOR
  • 目标:通过测试创建新功能
  • 起点:不存在代码
  • 测试类型:定义预期行为的规范测试
  • 结果:包含测试覆盖的新工作代码

DDD 方法(适用于重构):

  • 周期:分析-保留-改进
  • 目标:改进结构而不改变行为
  • 起点:行为明确的现有代码
  • 测试类型:捕获当前行为的特征测试
  • 结果:结构更完善且行为一致的代码
行为保留原则

DDD 的黄金法则是,重构前后的可观察行为必须完全一致。这意味着:

  • 所有现有测试必须原样通过
  • API 契约保持一致
  • 副作用保持一致
  • 性能特征保持在可接受范围内

实施指南

阶段 1:分析

分析阶段侧重于了解当前代码库的结构并识别重构机会。

识别领域边界

通过检查以下内容,识别代码库中的逻辑边界:

  • 模块依赖和导入模式
  • 组件间的数据流
  • 共享状态和耦合点
  • 公共 API 接口

使用 AST-grep 分析结构模式。对于 Python,搜索导入模式以了解模块依赖。对于类层次结构,分析继承关系和方法分布。

耦合度与内聚度指标

评估代码质量指标:

  • 传入耦合度(Ca):依赖此模块的类的数量
  • 传出耦合度(Ce):此模块依赖的类的数量
  • 不稳定性(I):Ce / (Ca + Ce)——值越高,表示越不稳定
  • 抽象度(A):抽象类 / 类总数
  • 与主序列的距离:|A + I - 1|

低内聚度和高耦合度表明这些代码可能需要重构。

结构分析模式

使用 AST-grep 识别有问题的模式:

  • 方法或职责过多的上帝类
  • 方法过度使用其他类数据的特性依恋
  • 表明缺少抽象的过长参数列表
  • 各模块间的重复代码模式

创建分析报告,记录:

  • 当前架构概览
  • 已识别的问题区域及严重程度评级
  • 拟议的重构目标及风险评估
  • 展示耦合关系的依赖关系图
阶段 2:PRESERVE(保留)

保留阶段会在做出任何更改之前建立安全网。

特征测试

特征测试会在不对正确性做任何假设的情况下捕获现有行为。目标是记录代码实际的行为,而不是它应该呈现的行为。

创建特征测试的步骤:

  • 步骤 1:通过执行识别关键代码路径
  • 步骤 2:创建测试以覆盖这些路径
  • 步骤 3:先让测试失败,以发现实际输出
  • 步骤 4:更新测试,使其预期结果与实际输出一致
  • 步骤 5:记录发现的任何意外行为

特征测试命名约定:testcharacterize[component]_[scenario]

行为快照

对于复杂输出,使用快照测试捕获当前行为:

  • API 响应快照
  • 序列化输出快照
  • 状态转换快照
  • 错误消息快照

快照文件在重构期间充当行为契约。

测试安全网验证

进入改进阶段之前,请确认:

  • 所有现有测试均通过(100% 通过)
  • 新的特征测试覆盖了重构目标
  • 受影响区域的代码覆盖率达到阈值
  • 安全网中不存在不稳定测试

如果有条件,请运行变异测试以验证测试的有效性。

阶段 3:IMPROVE(改进)

改进阶段会进行结构性更改,同时持续验证行为是否得到保留。

渐进式转换策略

切勿一次性进行大规模更改。遵循以下模式:

  • 进行尽可能小的结构性更改
  • 运行完整测试套件
  • 如果测试失败,立即回退
  • 如果测试通过,提交更改
  • 重复以上步骤,直到达到重构目标
安全的重构模式

提取方法:当代码块可以命名并独立出来时使用。使用 AST-grep 搜索重复代码块或过长方法,以识别候选项。

提取类:当一个类承担多项职责时使用。在保持原有 API 不变的前提下,将相关方法和字段移至新类,并通过委托维持原有 API。

移动方法:当一个方法使用其他类的数据多于自身数据时使用。移动方法,同时保留所有调用位置。

内联重构:当间接调用增加复杂度却没有带来益处时使用。将委托替换为直接实现。

重命名重构:当名称无法反映当前理解时使用。使用 AST-grep rewrite 原子化地更新所有引用。

AST-grep 辅助转换

使用 AST-grep 进行安全且具备语义感知的转换:

对于方法提取,创建一条规则,用于识别代码模式并将其重写为提取后的形式。

对于 API 迁移,创建一条规则,用于匹配旧 API 调用并将其重写为新 API 格式。

对于弃用处理,创建规则以识别已弃用的模式并建议现代替代方案。

持续验证循环

每次转换后:

  • 运行单元测试(快速反馈)
  • 运行集成测试(验证行为)
  • 运行特征测试(快照对比)
  • 确认没有引入新的警告或错误
  • 如适用,检查性能基准

DDD 工作流执行

标准 DDD 会话

通过 moai:2-run 以 DDD 模式执行 DDD 时:

步骤 1 - 初始评估:

  • 阅读 SPEC 文档,了解重构范围
  • 确定受影响的文件和组件
  • 评估当前测试覆盖率

步骤 2 - 分析阶段执行:

  • 对目标代码运行 AST-grep 分析
  • 生成耦合度和内聚度指标
  • 创建领域边界图
  • 记录重构机会

步骤 3 - 保留阶段执行:

  • 验证所有现有测试均通过
  • 为未覆盖的路径创建特征测试
  • 生成行为快照
  • 确认安全网足够完善

步骤 4 - 改进阶段执行:

  • 逐步执行转换
  • 每次更改后运行测试
  • 立即提交成功的更改
  • 记录发现的所有问题

步骤 5 - 验证与完成:

  • 运行完整测试套件
  • 对比更改前后的指标
  • 验证所有行为快照均匹配
  • 生成重构报告
DDD 循环模式

对于需要多轮迭代的复杂重构:

  • 根据范围设置最大循环迭代次数
  • 每轮循环聚焦于一个重构目标
  • 退出条件:所有目标均已处理或达到迭代次数上限
  • 通过更新 TODO 列表跟踪进度

质量指标

DDD 成功标准

行为保留(必需):

  • 所有已有测试均通过
  • 所有特征测试均通过
  • API 契约没有变化
  • 性能保持在界限内

结构改进(目标):

  • 降低耦合度指标
  • 提高内聚度评分
  • 降低代码复杂度
  • 更好地分离关注点
DDD 专项 TRUST 验证

以 DDD 为重点应用 TRUST 5 框架:

  • 可测试性:特征测试覆盖率足够
  • 可读性:已验证命名和结构改进
  • 可理解性:领域边界更加清晰
  • 安全性:没有引入新的漏洞
  • 透明度:所有更改均有记录且可追溯

集成点

与 AST-Grep 技能集成

DDD 高度依赖 AST-grep 来实现:

  • 结构化代码分析
  • 模式识别
  • 安全的代码转换
  • 多文件重构

规则位于 .moai/config/astgrep-rules/;使用 moai ast-grep 进行扫描,并使用 moai ast-edit 应用重写。

与测试工作流集成

DDD 与测试工作流相辅相成:

  • 使用测试模式中的特征测试
  • 与变异测试集成,以验证安全网
  • 利用快照测试基础设施
与质量框架集成

DDD 的输出可用于质量评估:

  • 对比更改前后的指标
  • 使用 TRUST 5 验证更改
  • 跟踪技术债务

故障排除

常见问题

转换后测试失败:

  • 立即还原到最近的已知良好状态
  • 分析哪些测试失败以及失败原因
  • 检查转换是否意外改变了行为
  • 考虑采用更小的转换步骤

特征测试不稳定:

  • 找出非确定性的来源
  • 模拟外部依赖
  • 修复依赖时间或顺序的行为
  • 考虑快照容差设置

性能下降:

  • 对比更改前后的性能
  • 找出受更改影响的热点路径
  • 考虑缓存或优化
  • 记录可接受的权衡
恢复流程

DDD 会话遇到问题时:

  • 使用 git stash 保存当前状态
  • 重置到最近一次成功提交
  • 检查导致失败的转换
  • 规划替代方案
  • 从已保存的状态继续

版本:1.0.0 状态:Active

常见的自我辩解

自我辩解实际情况
“这段遗留代码显然有问题,我直接重写就行”看起来有问题的代码可能承载着调用方依赖的生产环境行为。先进行分析,再编写测试后重写。
“特征测试只是白费功夫”特征测试是安全网,让你能放心重构。没有它们,重构就等于重写。
“我足够了解这个模块,可以跳过分析”理解不等于验证。分析会促使你明确假设,并审视调用方。
“这个函数没有测试,因为它没有值得测试的行为”只要有调用方,就有契约。需要刻画的正是这个契约。
“我会小心处理,以保留原有行为”小心不是一种保障机制。只有测试能捕获回归问题。
“这次重构太小,不需要 DDD”小规模重构恰恰会因人们跳过安全网而造成悄无声息的回归。

切斯特顿围栏原则:不要移除或替换你无法解释其用途的代码。设置分析阶段,正是为了找出那些看起来有问题的代码背后的原因。

危险信号

  • 重构提交没有新增测试,也没有修改任何现有测试
  • 缺少分析产物,或分析产物被缩减为一句话总结
  • 特征测试只断言返回值,忽略副作用和状态变化
  • 改进阶段引入了没有测试覆盖的新公共 API
  • 声称保留了原有行为,却没有提供重构前后的测试证据
  • 改进阶段删除了遗留测试,而不是更新它们

验证

  • 分析产物存在,并列出所有调用修改后代码的调用方
  • 改进开始前,所有公共入口点都已有特征测试
  • 特征测试在重构前的提交上通过(已验证基线)
  • 每个改进步骤完成后,特征测试仍然通过
  • 没有任何现有测试在未提供等效替代的情况下被删除
  • 修改后软件包的覆盖率不低于变更前
  • 至少有一条测试断言能捕获副作用(I/O、全局状态)

评论

支持 Markdown