← codebase-design · 技能图谱

Deepening

加深

How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in SKILL.mdmodule, interface, seam, adapter.

如何根据依赖关系安全地加深一组浅模块。假设使用 SKILL.md 中的词汇——模块接口接缝适配器

Dependency categories

依赖分类

When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.

在评估加深候选项时,对其依赖进行分类。类别决定了加深后的模块如何在其接缝处进行测试。

1. In-process

1. 进程内

Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed.

纯计算、内存状态、无 I/O。始终可加深——合并模块并通过新接口直接测试。无需适配器。

2. Local-substitutable

2. 本地可替代

Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.

具有本地测试替身的依赖(PGLite 替代 Postgres,内存文件系统)。如果替身存在则可加深。加深后的模块在测试套件中使用替身运行进行测试。接缝是内部的;模块的外部接口处没有端口。

3. Remote but owned (Ports & Adapters)

3. 远程但自有(端口与适配器)

Your own services across a network boundary (microservices, internal APIs). Define a port (interface) at the seam. The deep module owns the logic; the transport is injected as an adapter. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.

跨网络边界的自有服务(微服务、内部 API)。在接缝处定义一个端口(接口)。深模块拥有逻辑;传输层作为适配器注入。测试使用内存适配器。生产环境使用 HTTP/gRPC/消息队列适配器。

Recommendation shape: "Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."

推荐形态:"在接缝处定义一个端口,为生产环境实现 HTTP 适配器,为测试实现内存适配器,这样逻辑就集中在一个深模块中,即使它跨网络部署。"

4. True external (Mock)

4. 真正外部(模拟)

Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.

你无法控制的第三方服务(Stripe、Twilio 等)。加深后的模块将外部依赖作为注入的端口;测试提供模拟适配器。

Seam discipline

接缝纪律

  • One adapter means a hypothetical seam. Two adapters means a real one. Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
  • 一个适配器意味着假设的接缝。两个适配器意味着真正的接缝。 除非至少有两个适配器是合理的(通常是生产 + 测试),否则不要引入端口。单一适配器的接缝只是间接层。
  • Internal seams vs external seams. A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.
  • 内部接缝 vs 外部接缝。 一个深模块可以既有内部接缝(对其实现私有,供其自身测试使用),也有其接口处的外部接缝。不要仅仅因为测试使用内部接缝就通过接口暴露它们。

Testing strategy: replace, don't layer

测试策略:替换,不堆叠

  • Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them.
  • 一旦存在在加深后模块接口处的测试,浅模块上的旧单元测试就变成了浪费——删除它们。
  • Write new tests at the deepened module's interface. The interface is the test surface.
  • 在加深后模块的接口处编写新测试。接口就是测试面
  • Tests assert on observable outcomes through the interface, not internal state.
  • 测试通过接口断言可观察的结果,而不是内部状态。
  • Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.
  • 测试应该能在内部重构后存活——它们描述行为,而不是实现。如果实现变化时测试不得不跟着变,那它就是在越过接口测试。