Skip to main content
BlockX 仓库用三条设计原则约束 spec 和代码。它们不是抽象口号:仓库的 AGENTS.mddocs/specs/code-style.md 把它们展开成了具体的模块边界、依赖规则、错误语义和时间语义。这一页是这两份文件的导读,帮你在写代码前知道”什么会被 review 打回来”。

三条原则

新子系统先定义纯内存 core,用显式事件和命令描述行为,再在外面包 adapter。core 只维护状态机、幂等判定、阶段推进和结果收敛;RPC、UDS、etcd、数据库、时钟、日志、指标全部留在 adapter。仓库里的 WorkerCoreinternal/worker/core/)、CoordinatorCoreinternal/coordinator/core/)、DispatcherCoreinternal/worker/core/dispatcher.go)都是这种形态。它们是代码边界,不是新增进程或角色。
IO 访问这类以”谁拥有资源、什么时候释放”为中心的模块,不强行拆成事件/命令流。它们显式建模 Worker 级共享作用域和 task 级局部作用域,把 quota、cache、singleflight、deadline 和清理绑定到作用域生命周期上。作用域退出是结构性清理边界,不依赖”稍后再发一条释放命令”。internal/io/ 是这种形态,入口是 WorkerIOScope / TaskIOScope 这样的同步接口。
如无必要,勿增实体。新增 struct、interface、错误码、队列、状态或服务前,先问现有概念能不能复用或简化。如果一个新实体说不清”为什么现有 module / core / adapters / scope 边界装不下它”,先不要加。
判断用哪条原则的经验法则来自 AGENTS.md:如果一个行为是生命周期重的(slot、task、call 的状态推进),用显式状态、事件、命令表达;如果它是资源治理重的(连接池、配额、缓存),用显式作用域拥有权、获取和清理规则表达;如果两种框架都给不出一组小而可枚举的规则,说明设计还太模糊。

分层与依赖规则

不要引入没有明确边界的 pkg/utilscommonshared 汇总目录。仓库里现有的 internal/common/ 只放 argspoolclientidtypesunsafebytes 这几个有明确用途的小包,不是通用杂物箱。

adapter 与 core 的交互模式

推荐”输入事件 → core 决策 → adapter 执行命令”的单向模式,而不是让 core 持有一组反向 callback。docs/specs/code-style.md 给的示意:
定时器、Executor、Plugin 或 IO 回调完成后,adapter 把结果重新包装成 core 事件再喂回去。这样 core 保持单线程、可重放、可表驱动测试。 具体的事件名和命令名以各组件页为准:WorkerCoordinatorCall 执行子系统

interface 使用

  • interface 定义在真正的消费侧。
  • adapter 调用 core 时通常不需要再为 core 抽 interface,直接依赖 WorkerCoreCoordinatorCore 等具体类型。
  • 只有当 core 确实需要一个同步 port,且这个 port 能保持纯、窄、可替换时,才在 core/ 内定义最小 interface 由 adapter 实现。
  • 没有第二个实现或明确测试替身需求前,不要为”以后可能扩展”提前抽接口。

命名与建模

  • 包名短、稳定、领域化,优先名词。
  • 导出标识符用明确名字,避免 ManagerUtilDataInfo 这类弱语义后缀。
  • 状态、事件、命令直接反映领域语义,例如 TaskPhaseFinishedPrepareTaskActivationReleaseSlot
  • 优先显式 struct 和命名枚举,不滥用 map[string]anyany 或零散字符串字面量。
  • 需要多个布尔字段组合才能理解对象状态时,改成显式状态枚举。
  • NewXxx 只在能建立不变量时使用;字面量包装不必强行写构造器。

错误与结果表达

  • 先区分”准入 / 协议错误”和”已进入执行后的终态结果”,不要混成一套错误返回。在 Worker 上,边界是 slot 是否已从 ALLOCATED 激活到 RUNNING:激活前的错误是提交 / 准入层错误,激活后的失败统一进入 TaskResult
  • 稳定语义依赖错误码、枚举或命名结果类型,不依赖错误字符串。
  • 对外协议已有稳定错误码约定时以协议字段为准,不在 adapter 内再发明平行语义。
  • 在 Sans-IO core 里,预期业务结果建模成显式结果,不滥用 error
  • TaskResultInvalidSlotNoSlotUnavailable 等边界必须与 docs/specs/architecture.mddocs/specs/worker.md 一致。

时间语义

  • 时间语义集中建模,不散落在 handler、Plugin、driver 各自判断。
  • Worker 侧的 slot TTL、task deadline、结果保留窗口收敛在 Worker 模块及其 timer adapter,以 docs/specs/worker.md 为准。
  • Coordinator 侧的视图新鲜度、候选重试和选址相关时间语义收敛在 Coordinator 模块,以 docs/specs/task-resource-coordinator.md 为准。
  • 在 scope-oriented 模块里,生命周期结束优先靠 scope Close()、context 取消和结构性 cleanup 保证,再辅以迟到结果防御。
  • goroutine 的创建、退出和回收路径必须清楚,不能把生命周期管理留给进程退出兜底。

测试与文档同步

  • core/ 测试重点覆盖状态迁移、幂等、TTL、deadline 和失败收敛,优先表驱动。
  • scope-oriented 模块测试重点覆盖 quota 获取 / 释放、scope 关闭、迟到结果丢弃、singleflight 行为和资源无泄漏。
  • adapter 测试重点覆盖协议映射、错误翻译、命令执行编排,不重复 core 已覆盖的状态组合。
  • 修改协议或生命周期语义时,测试与对应 spec 在同一变更中一起更新。
具体命令和测试分层见 测试

相关文档

  • blockx 仓库 AGENTS.md:仓库级原则、目录骨架、测试基线、commit / PR 规范。
  • blockx 仓库 docs/specs/code-style.md:本页展开的来源,含 api/ 边界、Sans-IO 与 Scoped Resource Context 的具体约束。
  • blockx 仓库 docs/specs/io-subsystem.md:Scoped Resource Context 的完整定义。
  • 仓库结构与代码分层
  • 贡献流程