关于 composition-root
用于整理 Cloudflare 应用架构的编码 Skill,围绕组合根和生命周期范围内的依赖展开。它指导开发者通过应用自有端口隔离平台绑定,并在各运行时入口验证边界。
主要功能
- 01梳理入口、绑定与生命周期
- 02定义应用自有端口和依赖
- 03分离绑定适配器与应用策略
- 04为运行时入口组合依赖
- 05通过测试与搜索验证边界
适用场景
- 01重构直接使用 Cloudflare 绑定的服务
- 02在 Worker 和定时入口接入服务
- 03避免应用模块依赖运行时类型
- 04检查追踪与副作用的归属
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ alexander-zuev/ agent-skills --skill composition-root安装后在 Claude Code 中输入/composition-root确认已加载。
附带文件EXAMPLES.md
详细介绍
Cloudflare 组合根
使用组合根,在运行时能力所属的生命周期内构建应用依赖项。
在放置构造或调用代码前,先应用识别系统入口点并引导依赖项。
内部代码接收端口,而不是 Env、原始绑定、框架服务定位器或绑定名称。
Cloudflare entrypoint
→ tracer + parsed configuration
→ binding adapters
→ exact service dependencies
→ application service
→ protocol projection
对 TypeScript 契约、解析、错误、副作用和测试,请应用 ../typescript-standards/SKILL.md。如果实现形式不明确,请参阅 EXAMPLES.md,了解 TanStack Start、WorkerEntrypoint、适配器和重构模板。
所有权
- **组合根:**为其所属作用域构建适配器和服务,并将其提供给操作入口点。
- **入口点适配器:**解析外部输入、建立调用上下文、使用提供的依赖项调用服务,并转换其结果。
- **绑定适配器:**使用 KV、R2、D1、Durable Objects、Queues、服务绑定、Workflows 或其他外部能力,实现应用自有端口。
- **应用服务:**负责一个用例的执行顺序、回退、重试和尽力而为策略。
- **领域模块:**负责纯值和不变量。
- **端口:**准确命名使用方所需的能力;不照搬 Cloudflare API。
原始平台值只能存在于组合根和绑定适配器中。绑定适配器只接收最小的平台能力,而不是整个 Env。
1. 绘制边界图
检查仓库说明以及受影响的:
- TanStack Start 的
src/server.ts、请求上下文类型、中间件顺序和服务端函数; - Worker、WorkerEntrypoint、Durable Object、Workflow、队列和定时入口点;
Env声明以及相关绑定的所有使用处;- tracer 的创建、关联、错误报告和脱离调用的工作所有权;
- 现有服务、适配器、端口、测试和架构检查。
追踪每种受影响行为从入口点到副作用和调用方可见结果的完整路径。重构前,记录当前的错误、重试、回退、序列化和追踪行为,再进行迁移。
**完成标准:**已清点每个受影响的入口点、绑定使用方、行为、生命周期、副作用、失败策略和现有测试切入点。
2. 设计应用自有依赖项
根据使用方的需求定义契约:
/** Persistence capability required by document application services. */
export interface DocumentStore {
/** Find one document by its domain ID. */
find(id: DocumentId): Promise<Document | null>;
/** Persist one valid document. */
save(document: Document): Promise<void>;
}
/** Exact dependencies of the publish-document use case. */
export type PublishDocumentDependencies = Readonly<{
documents: DocumentStore;
clock: Clock;
tracer: Tracer;
}>;
规则:
- 使用领域输入和已解析的输出,而不是原始键、字符串、请求对象或绑定选项。
- 仅公开实际调用方所需的操作。
- 精确声明每个服务的依赖对象;绝不创建共享的巨型对象,也不要将
Env重命名为Dependencies。 - 将平台序列化、键空间、分页令牌、TTL、元数据和输出解析放在绑定适配器中。
- 由应用服务负责策略。适配器应准确转换失败,以便服务区分可恢复情况、数据损坏和缺陷。
- 将
Tracer注入创建 span 的服务和适配器;不要把它添加到每个方法签名中。 - 仅当组合后作用域确实会动态变化时,才添加应用自有工厂,例如身份验证后按账户构造对象。工厂不得暴露
Env或原始绑定。
明确选择生命周期:
- 当绑定和 tracer 可安全地跨并发调用使用时,复用不可变的适配器和服务。
- 当对象保留请求作用域的运行时对象或可变调用状态时,按调用构造。
- 将脱离调用的工作交给明确的所有者,例如范围狭窄的后台任务端口;不要将
ExecutionContext泄漏到内部。
**完成标准:**所有内部签名都使用应用/领域类型;每项操作和工厂都有当前调用方;依赖项、tracer 和副作用的生命周期都有明确的所有者。
3. 构建或重构
新服务分支
- 针对端口实现应用服务,不导入 TanStack 或 Cloudflare 运行时类型。
- 为每个外部角色实现一个绑定适配器。为特定技术的实现指定明确名称,例如
WorkersKvDocumentStore。 - 将协议相关逻辑保留在 HTTP、RPC、队列、Workflow 或定时调度适配器中。
- 将组合后的服务提供给每个承载该用例的运行入口。
现有代码分支
- 如果缺少行为依据,通过在当前公共接口处编写测试,保留调用方可见的行为。
- 根据直接使用方现有的操作,在其所在位置定义端口。
- 在更改策略之前,将现有原始绑定置于适配器之后。
- 将直接使用方的原始依赖替换为端口。
- 每次向外移动一层适配器的构造。中间层可以传递类型化端口,但不能将端口和原始绑定都作为永久设计的一部分。
- 拆分职责混杂的模块:策略移至应用服务;平台机制移至适配器;协议映射留在入口点。
- 最后一个调用方迁移后,移除过时的
Env参数、工厂、通用包装器、重复的解析器/键构造器和兼容性重载。
不要仅为追求对称性而将重构扩展到无关绑定。
完成标准: 新代码已在每个服务入口完成组合,或每个已迁移的原始绑定都只会到达其适配器;不存在行为或策略变更,除非这些变更已明确提出并经过测试。
4. 组合每个运行时表面
在每个组合根中,按以下顺序进行:
- 解析配置和入口点参数。
- 创建或获取追踪器。
- 在调用被追踪的工作之前建立关联。
- 将原始绑定封装在适配器中,并在需要时注入追踪器。
- 组装准确的服务依赖项。
- 通过精简的协议适配器调用服务。
- 将结果和安全错误映射为外部契约。
对于 TanStack Start:
- 在
src/server.ts中为每次 fetch、队列和定时调度调用使用一个工厂(例如createAppDeps(env, ctx))构建依赖图。 - 将依赖图作为请求上下文传递给 Start 处理程序,并通过
Register声明其类型。 - 服务端函数和中间件从
context.deps读取准确的功能接口。它们绝不读取env或绑定名称。 - 注入根追踪中间件所用的追踪器,不要在应用更深处另行创建无关的追踪器。
根据依赖项的生命周期选择组合根,而不是根据构造函数或处理程序的名称来选择。 在各个运行入口复用同一个依赖工厂;不要在每个路由或 SDK 回调中单独构建依赖图。
完成标准: 每个服务入口都能接收到其作用域对应的依赖图;没有任何内部模块获取运行时资源或读取绑定名称。
5. 验证边界
通过生产边界进行验证:
- 使用实现真实端口的小型记录型伪件,验证应用服务行为;
- 针对有代表性的本地 Cloudflare 运行时,验证绑定适配器的序列化、解析和故障处理;
- 验证入口点组合以及外部结果/错误映射;
- 验证 span 名称、关联、安全属性和分离任务的所有权;
- 执行类型检查、针对性测试、架构检查以及仓库要求的验证。
使用项目的绑定名称和运行时类型搜索泄漏,例如:
rg 'Env|KVNamespace|R2Bucket|D1Database|DurableObjectNamespace|ExecutionContext' src
对每个匹配项进行分类:组合根、绑定适配器、不可避免的框架声明,或违规项。还要搜索路由、服务、领域和功能接口模块中对 context.env 和绑定名称的直接访问。
完成标准: 每个原始平台引用都已分类且符合要求;每个已更改的端口、适配器、服务、入口点、故障路径和生命周期都有依据;并且没有应用模块导入或接收 Cloudflare 运行时类型。