name: growing-outside-in-systems description: > 使用外向内测试驱动开发与六边形架构驱动功能开发。 设计通过内联代码、内存假对象、接口提取和延迟I/O逐步浮现。 适用于构建功能、编写测试或构建后端服务时使用。 触发词:TDD、外向内、六边形架构、端口与适配器、浮现式设计、 验收测试、组件测试、行走骨架、内存假对象、 组件、契约测试、适配器、快速测试、亚秒级反馈。 语言无关(Go、Rust、Python、TypeScript、Java、C#)。 allowed-tools: Read Glob Grep WebFetch metadata: author: giladw version: “1.0.0” category: backend-development backlog-id: SK-GOIS-001
STARTER_CHARACTER = 🔴🟢
外向内TDD与六边形架构
通过从外向内驱动设计来构建功能。每个功能都从一个失败的验收测试开始。设计通过严格的红-绿-重构循环逐步浮现。基础设施延迟到领域API被验证后才实现。
与使用模拟对象的自内向外TDD不同,这种方法在边界测试行为——而非实现细节——使测试套件天生支持重构。参见methodology.md。
关于贯穿始终的规范术语,参见references/glossary.md。
两个循环
外循环(验收测试): 在服务/系统边界编写一个失败的范围测试。该测试使用内存适配器跨限界上下文进行集成测试。它定义了“完成”的标准。
内循环(红-绿-重构): 在限界上下文内循环以使外部测试通过。仅当需要实现验收测试要求时才进入此循环。
每个功能都从外向内开始。验收测试驱动整个过程。
测试优先级
验收测试和组件测试是主要工具。单元测试是例外。
- 验收测试 — 服务/系统边界,进程内,内存适配器;作为规范阅读
- 组件测试 — 单个限界上下文;通过内存适配器驱动内部模块设计
- 单元测试 — 仅例外情况:良好隔离、真正复杂、算法或组合逻辑
完整定义、测试矩阵和契约测试模式参见testing-strategy.md。
测试矩阵
| 快速 | 慢速 | |
|---|---|---|
| 大范围 | 验收与组件 — 日常使用 | E2E — 最小化 |
| 小范围 | 单元 — 谨慎使用 | 契约 — 仅CI |
亚秒级反馈是不可妥协的。 如果测试需要几秒钟,团队就会停止重构,系统就会衰退。
浮现式设计工作流
在每个限界上下文中严格遵循此顺序:
- 内联 — 在业务代码中朴素地实现逻辑;使用局部变量、列表、映射;无抽象
- 内存适配器 — 一旦行为被验证,将逻辑提取到专用的内存适配器实现中
- 接口 — 从实际浮现的内容中提取适配器接口(而非理论猜测)
- 检查点: 接口中的所有类型必须位于六边形内部
- I/O检查点: 如果没有I/O且不运行在进程外 → 保持在六边形内部;不要创建端口
- 延迟I/O — 仅在API成熟后实现真正的适配器(数据库、网络、文件系统)
- 重构六边形内部的内联代码
此顺序是不可妥协的。 每个步骤收集关于适配器实际需求的证据。详细原理和行走骨架参见methodology.md。
六边形架构
领域层是中心。所有I/O都位于边界后的适配器接口之后。
依赖规则: 基础设施(适配器) → 应用(端口) → 领域(外层依赖内层,绝不反向)
I/O分类规则: 如果不执行I/O且不运行在进程外 → 属于六边形内部;否则 → 适配器。
端口引用的类型必须位于六边形内部。 如果端口签名引用适配器层中的类型,六边形就向外依赖——这是违规。
命名:
- 端口:
For[某事物](例如:ForCalculatingTaxes、ForGettingTaxRates) - 适配器:
[某事物]Adapter(例如:WebUIAdapter、SQLDatabaseAdapter)
完整文件夹结构、私有/公共六边形拆分、跨六边形依赖规则、适配器设计模式和测试接缝参见folder-structure.md。
内存假对象,而非模拟对象
对所有适配器边界使用内存假对象。领域级测试绝不使用模拟对象或桩对象。
假对象是基于简单数据结构(映射、列表)的真实实现。它们具有实际行为。模拟对象仅验证调用序列——它们将测试耦合到实现细节,每次重构都会破坏测试。
契约测试验证真实适配器与假对象的行为匹配。参见testing-strategy.md。
行走骨架
对于新项目或主要新组件:首先构建一个最小的端到端实现,以提前支付集成成本。一旦建立,转向浮现式设计工作流。参见methodology.md。
组合根(配置器)
在启动时连接所有适配器接口。用于两种上下文:
- 生产: 真实适配器实现(数据库、网络、文件系统)
- 测试: 内存适配器实现 → 快速、确定性的测试工具
相同的业务逻辑在两种上下文中运行——仅适配器不同。
功能实现顺序
- 编写失败的验收测试 — 定义“完成”
- 进入内循环 — 红-绿-重构
- 遵循浮现式设计顺序 — 内联 → 内存 → 接口 → 延迟I/O
- 如果需要,添加组件测试 — 当限界上下文变得复杂时
- 仅在合理时添加单元测试 — 复杂算法、组合逻辑
- 最后实现真实适配器 — 通过与内存假对象的契约测试进行验证
倾听测试
编写测试困难是设计信号,而非技能问题:
- 难以设置 → 依赖过多;拆分或重新思考适配器边界
- 难以断言 → API隐藏或纠缠了调用者需要的信息
- 脆弱,重构时破坏 → 测试实现细节;升级到组件/验收测试
- 缓慢 → 访问真实I/O;找到缺失的适配器边界
不要与测试对抗。重塑代码直到测试易于编写。
参考文档
- glossary.md — 规范术语
- folder-structure.md — 文件夹结构、私有/公共六边形、跨六边形规则、适配器模式、测试接缝
- testing-strategy.md — 完整测试矩阵、验收/组件测试规则、契约测试模式
- methodology.md — 为什么选择外向内而非常规TDD、行走骨架、浮现式设计深度解析、倾听测试、反模式
来源
- Growing Object-Oriented Software, Guided by Tests — Steve Freeman & Nat Pryce (2009)
- Big Design Up-Front or Emergent Design? Hexagonal Architecture Gives Us Both — Shai Yallin (2020)
- Hexagonal Architecture — Alistair Cockburn (2005)
- Contract Test — Martin Fowler
- Component Test — Martin Fowler
- Bounded Context — Martin Fowler