# 拆解算法(P1) 把用户需求变成一棵可执行的模块树。拆解的目的是:**让树上的每一个叶子模块都小到可以由一个 agent 独立完成"设计 + 代码 + 自测"**。 ## 1. 拆解步骤 1. **定边界**:从需求中圈出系统边界(做什么、不做什么),技术栈、运行环境、验收方式。 2. **第一层拆分**:按**职责**(不是按文件/按页面)切第一层模块。候选维度: - 业务域(用户、订单、支付、通知) - 技术层(协议层、数据层、业务层、展示层)——仅在职责确实不同时使用 - 独立可运行的子系统(CLI、服务端、前端、迁移工具) 3. **逐层递归**:对每个"仍然太大"的模块重复拆分,直到叶子满足终止条件。 4. **登记**:把结果写进 `.mdd/manifest.md`(模板见 `templates/module-manifest.md`)。 ## 2. 叶子模块终止条件(4 条全部满足才停) | # | 条件 | 判断方法 | 不满足时的动作 | |---|------|----------|----------------| | 1 | **单一职责** | 能否用一句话说清"这个模块做什么",且只有一类职责 | 继续按职责切分 | | 2 | **接口可枚举** | 对外 API / 数据结构 / 配置项能在一页内列全 | 继续拆到接口变小 | | 3 | **体量可控** | 预计 1–5 个文件、数百行以内,一个 agent 单轮能完整生成并自测 | 继续拆 | | 4 | **依赖已定义** | 依赖的模块接口已经定稿(在依赖方向上先设计) | 先完成上游设计,或拆出未定稿部分 | **经验锚点**:超过约 5 个文件 / 数百行 / 接口超过一页,通常就该拆。宁可多拆一层,也不要让一个 agent 一次生成太多(一次生成越多,契约漂移和返工成本越高)。 ## 3. 模块命名与 id - id 用 kebab-case 路径,语义化:`auth`、`auth/oauth`、`ui/chat-input`、`data/orders-store`。 - 模块名 = 职责短语,不是文件名。 - 父子关系体现在 id 前缀:`auth/oauth` 是 `auth` 的子模块。 ## 4. 依赖规则 - **单向依赖**:模块只依赖"更基础"的模块;依赖图不允许成环(循环 = 设计错误,先合并或抽公共模块)。 - **接口先于实现**:被依赖方(上游)先设计、先定稿契约,下游才可开工。 - **依赖最小化**:只声明真正用到的模块;两个模块之间的通信只通过各自 design.md 里写明的契约。 - **共享类型/工具**:出现多个模块都需要的公共概念(枚举、DTO、错误码、utils),抽成独立的公共模块(如 `common/contracts`、`common/utils`),被各方依赖。 ## 5. 常见反例 | 反例 | 问题 | 正确做法 | |------|------|----------| | 按文件/页面拆("三个页面对应三个模块") | 页面之间共享逻辑无处安放,接口混乱 | 按职责拆:页面只是表现层,逻辑归业务模块 | | 一个模块塞"全部工具函数" | 没有单一职责,接口无穷 | 按用途拆(如 `common/utils/strings`、`common/utils/time`) | | 拆得太细(一个函数一个模块) | 派单开销大于收益 | 合并到体量下限:一个模块至少是一个可独立测试的单元 | | 环依赖(A↔B) | 无法确定设计顺序,无法独立生成 | 提取公共部分 C,让 A、B 都只依赖 C | | 未定稿契约就派单 | 下游基于猜测实现,必然返工 | 先补上游设计,再派下游 | ## 6. 输出校验(拆解完成时检查) 1. 叶子模块全部满足终止条件 4 条; 2. 依赖图无环,且每个 `depends_on` 引用的模块存在于 manifest; 3. 每个模块在 manifest 有唯一 id 与状态 `planned`; 4. 整个需求(含非功能需求:性能、安全、可观测性)都有模块或明确说明其归属。