| name | codebase-design |
| description | 设计深层模块的共享词汇。适用于用户想要设计或改进模块接口、寻找深化机会、决定接缝位置、使代码更可测试或对 AI 更可导航,或其他技能需要深层模块词汇的场景。 |
代码库设计
设计深层模块:大量行为隐藏在小型接口之后,放置在清晰的接缝处,通过该接口可测试。在任何设计或重构代码的地方使用这套语言和原则。目标是为调用者提供杠杆,为维护者提供局部性,为所有人提供可测试性。
词汇表
严格使用这些术语——不要替换为"组件"、"服务"、"API"或"边界"。一致的语言是全部要点。
模块——任何有接口和实现的东西。刻意与规模无关:一个函数、类、包或跨层切片。避免:单元、组件、服务。
接口——调用者正确使用模块必须知道的一切:类型签名,还有不变量、排序约束、错误模式、所需配置和性能特征。避免:API、签名(太窄——它们仅指向类型层面的表面)。
实现——模块内部的内容,其代码体。区别于适配器:一个东西可以是一个小适配器带大实现(一个 Postgres 仓库)或一个大适配器带小实现(一个内存假对象)。当接缝是主题时用"适配器";否则用"实现"。
深度——接口处的杠杆:调用者(或测试)每学习单位接口能运用的行为量。一个模块是深的,当大量行为隐藏在小型接口之后;是浅的,当接口几乎和实现一样复杂。
接缝 (Michael Feathers)——一个可以在不编辑该处的情况下改变行为的地方;模块接口所在的位置。接缝放在哪里是其自身的设计决策,区别于接缝后面是什么。避免:边界(被 DDD 的有界上下文过载)。
适配器——在接缝处满足接口的具体事物。描述角色(它填补哪个槽位),而非实质(里面是什么)。
杠杆——调用者从深度中获得的东西:每学习单位接口获得更多能力。一个实现付出,在 N 个调用点和 M 个测试中获得回报。
局部性——维护者从深度中获得的东西:变更、bug、知识和验证集中在一处,而不是分散在调用者中。一处修复,处处修复。
深 vs 浅
深层模块 = 小接口 + 大量实现:
┌─────────────────────┐
│ 小型接口 │ ← 少数方法,简单参数
├─────────────────────┤
│ │
│ 深层实现 │ ← 复杂逻辑隐藏
│ │
└─────────────────────┘
浅层模块 = 大接口 + 少量实现(应避免):
┌─────────────────────────────────┐
│ 大型接口 │ ← 许多方法,复杂参数
├─────────────────────────────────┤
│ 薄实现 │ ← 只是传递
└─────────────────────────────────┘
设计接口时,问:
- 我能减少方法数量吗?
- 我能简化参数吗?
- 我能隐藏更多复杂性在内部吗?
原则
- 深度是接口的属性,而非实现的属性。 一个深层模块可以在内部由小型、可 mock、可替换的部件组成——它们只是不是接口的一部分。一个模块可以有内部接缝(对其实现私有,由其自己的测试使用)以及其接口处的外部接缝。
- 删除测试。 想象删除该模块。如果复杂性消失,它就是个传递。如果复杂性在 N 个调用者中重新出现,它就在赚取它的价值。
- 接口就是测试表面。 调用者和测试跨越同一接缝。如果你想测试到接口之外,模块的形状可能不对。
- 一个适配器意味着假设的接缝。两个适配器意味着真实的接缝。 除非某物确实在接缝处变化,否则不要引入接缝。
为可测试性设计
好的接口使测试变得自然:
-
接受依赖,而非创建依赖。
function processOrder(order, paymentGateway) {}
function processOrder(order) {
const gateway = new StripeGateway();
}
-
返回结果,而非产生副作用。
function calculateDiscount(cart): Discount {}
function applyDiscount(cart): void {
cart.total -= discount;
}
-
小表面积。 更少的方法 = 需要更少的测试。更少的参数 = 更简单的测试设置。
关系
- 一个模块有且仅有一个接口(它呈现给调用者和测试的表面)。
- 深度是模块的属性,对照其接口来衡量。
- 接缝是模块接口所在之处。
- 适配器位于接缝处并满足接口。
- 深度为调用者产生杠杆,为维护者产生局部性。
拒绝的框架
- 深度为实现行数与接口行数之比(Ousterhout):奖励填充实现。我们改用深度作为杠杆。
- "接口"作为 TypeScript
interface 关键字或类的公共方法:太窄——这里的接口包括调用者必须知道的每个事实。
- "边界":被 DDD 的有界上下文过载。说接缝或接口。
深入探索