docs(architecture): define reusable module contracts and adoption plan
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
---
|
||||
title: "当前系统模块解耦现状审查"
|
||||
type: docs
|
||||
date: 2026-09-22
|
||||
source_sha: d034aa00e5785f51234b484f9dca6bf4fb037660
|
||||
task_id: modular-contract-standard-2026-09-22
|
||||
---
|
||||
|
||||
# 当前系统模块解耦现状审查
|
||||
|
||||
## 结论与范围
|
||||
|
||||
当前系统属于**已有实质模块化基础,但契约一致性、数据所有权与运行隔离尚未闭合的模块化单体**。不是只有文件夹拆分:已有应用用例、共享事务、PostgreSQL outbox/delivery/inbox、独立 events 进程、PI Runner HTTP typed-result 边界。
|
||||
|
||||
下一步收益最大的工作是统一业务入口、修正事件顺序契约、补运行时校验和资源预算。把现有相互依赖的实现全部包装为 HTTP 服务,会保留语义耦合并引入跨进程失败窗口。
|
||||
|
||||
本报告针对固定源码 SHA,只读检查 Go/React、架构配置、迁移和部署定义。**未检查当前实际镜像/进程/数据库积压,未运行 Go/PG/PI/浏览器或故障注入测试。** 下文“实现存在”是源码证据;“可能造成”是风险推导;不宣称现网事故已复现,也不改变 FEATURE-MATRIX 或发布门禁。
|
||||
|
||||
关联:[通用规范](../system-design/2026-09-22-generic-module-contract-standard.md)、[采用规划](../remediation/2026-09-22-modular-platform-adoption-plan.md)、[任务与验证](../ops/short-term/2026-09-22-modular-contract-standard.md)。
|
||||
|
||||
## 分维度判断
|
||||
|
||||
| 维度 | 已有基础 | 当前局限 | 判断 |
|
||||
|---|---|---|---|
|
||||
| 模块职责 | domain/app/server 分层,组合根 | server 认识大量实现,跨包类型泄漏 | 部分解耦 |
|
||||
| 业务入口 | docflow/stageflow/chatflow 用例 | 对话和 HTTP 同一动作存在旁路 | 需优先收敛 |
|
||||
| 数据边界 | 表 owner 清单与 UoW | 5 项 SQL 写例外、16 项路径例外 | 明确但未闭合 |
|
||||
| 事件通信 | 可靠 PG 投递与独立进程 | 顺序规则与 ADR 冲突,校验不完整 | 机制具备,语义有缺口 |
|
||||
| 并发与幂等 | claim、lease、token、DB 收据 | 慢消费者共享循环,同 ID 异载荷不校验 | 部分保证 |
|
||||
| 跨进程边界 | Web/server、PI Runner、events | 业务模块大多仍共享 server 进程/资源 | 不能宣称全模块故障隔离 |
|
||||
| 前端功能 | API/hooks 拆分 | 根状态和 setter 跨功能共享 | 文件组织优于状态边界 |
|
||||
| 工程约束 | arch-check 与契约 registry | 静态覆盖有限,不证明运行隔离 | 有工具,须明确盲区 |
|
||||
|
||||
不提供无权重依据的“解耦百分比”。以下发现按对数据与行为的实际影响排序,而非按文件大小排序。
|
||||
|
||||
---
|
||||
|
||||
## A1. 事件领取规则与已接受的顺序契约相冲突
|
||||
|
||||
**高优先级源码问题。** ADR 规定允许首个高领域版本 bootstrap,禁止把 MemoryBus 的连续版本等待搬到 PG dispatcher:`.doc/adr/2026-09-15-event-consumer-checkpoint.md:12-23`。
|
||||
|
||||
当前 `internal/events/dispatcher.go:39-46,75-84` 采用 `version <= checkpoint + 1`;无 checkpoint 时只接纳版本 0/1。生产者 `internal/app/docflow/commands.go:182-185` 使用 document record ID,`:211-214` 使用 candidate ID,`:162-165` 使用文档账本版本。这些不是从 1 开始连续的事件序号。
|
||||
|
||||
因此在“无 checkpoint、第一条版本为 42”这一明确条件下,该事件按 SQL 不能被领取。是否已有这种实际积压未查数据库,不能报成现网观测。
|
||||
|
||||
现有 `internal/events/pg_test.go:18-28` 固化同样 gap 规则;`dispatcher_pg_test.go:105-109` 使用 v1/v2,不能覆盖稀疏首事件。
|
||||
|
||||
**建议验收**:真实 PG 下测试首次 v42、v42→v47、部分事件订阅、毒消息隔离;按既有 ADR 恢复 bootstrap,或经明确新 ADR 引入独立事件序列及迁移。通用规范对应 MCS-15。
|
||||
|
||||
## A2. 同一批准动作存在 HTTP 与对话入口分叉
|
||||
|
||||
**高优先级待验证风险。** HTTP `internal/server/handlers.go:793-808` 调用 `docflow.Commands.Approve`,后者在 `internal/app/docflow/commands.go:173-188` 执行 UoW 与 EventApproved。
|
||||
|
||||
对话 `internal/chat/action_executor.go:68-76` 直接调用 GovernanceEngine.ApproverApprove;`internal/app/app.go:294-307` 已不再注册旧通知/批准 hook,`internal/docgov/engine.go:957-960` 的直接路径仅尝试旧 hook。
|
||||
|
||||
源码证明入口路径不同,存在绕过 durable workflow_advance/notify 的风险;本次未执行真实对话批准,不能把完整用户后果记为已复现。
|
||||
|
||||
**建议验收**:同一授权场景分别从 HTTP、对话、后台任务触发,比较授权拒绝、状态提交、事件、工作流推进与通知,所有入口汇入同一用例。通用规范对应 MCS-04。
|
||||
|
||||
## A3. 契约登记尚未成为完整运行时校验
|
||||
|
||||
`internal/events/registry.go:28-52` 登记 producer、consumer、schema、PayloadFields;`:75-91` 主要检查启用与消费者。
|
||||
|
||||
`internal/events/envelope.go:65-85` 检查基础字段、schema、digest/scope,但不按事件逐项校验 producer allowlist、JSON 必填/类型/额外字段。`internal/events/pg.go:32-47` 依赖此验证。`inventory.go:29-63` 也未完成 payload 结构检查。
|
||||
|
||||
实际 `internal/app/docflow/commands.go:279` 的 Producer 为 `docflow`,registry 示例为 `app/docflow`(`registry.go:42-46`),当前路径不会因此拒绝。消费 hydration `dispatcher.go:204-224` 与 handler 调用 `worker.go:252` 也不能证明所有入口统一重新校验。
|
||||
|
||||
**建议验收**:未知事件/schema、伪造 producer、缺必填、异类型、超大 payload、digest/tenant/environment 不符均有发布和消费负例。通用规范对应 MCS-08/09/18/21。
|
||||
|
||||
## A4. 消费者逻辑独立,但资源执行仍串行共享
|
||||
|
||||
`internal/events/worker.go:99-113` 逐一 RunOnce 各消费者;`:137-147` 一批先 claim 再逐个 handle。心跳直到该条 handle 才启动(`:208-234`)。
|
||||
|
||||
慢消费者会延迟同 worker 的其他消费者;预取队尾还可能在开始处理前耗掉 lease。增加副本能缓解吞吐,不证明消费者有独立预算和公平性。当前可隔离永久错误,不等于慢任务也隔离。
|
||||
|
||||
**建议验收**:阻塞一个消费者、保持另一个持续输入,后者满足自己的延迟预算;批量排队超过租期时不允许并发重复副作用。先按空闲槽 claim,并配置每消费者超时、并发、池配额;高风险任务再进程隔离。对应 MCS-14/17。
|
||||
|
||||
## A5. 去重与健康检查不能被扩大解释
|
||||
|
||||
`internal/events/pg.go:43` 对相同 event_id 使用 DO NOTHING,未比较已有 payload digest。因此“重复 ID 被忽略”并不保证“同键异载荷会冲突”。
|
||||
|
||||
`cmd/events/main.go:91-115` 的 health、ready、live 分别关注进程、DB/schema/注册条件、调度心跳;`worker.go:109-112` 在处理前 Touch 且忽略 RunOnce 返回错误。Compose healthcheck 使用 /health(`docker-compose.dev.yml:170-175`)。
|
||||
|
||||
**建议验收**:同 ID 同内容返回既有结果、不同内容冲突;依赖持续失败时最老消息年龄和分消费者失败数告警,不能依靠健康探针证明业务前进。对应 MCS-13/19/20。
|
||||
|
||||
---
|
||||
|
||||
## B1. 组合根、契约类型与实现仍相互渗透
|
||||
|
||||
优点:`internal/app/app.go:96` 集中装配,`internal/server/server.go:87` 接收 runtime。限制:`server.go:40-84` 持有领域具体服务、pool、上下文等,`:123-132` 还回填 Runtime 并初始化执行器。HTTP 适配器获得整个 Runtime,难以维持最小依赖。
|
||||
|
||||
`internal/app/stageflow/usecase.go:10-37` 是窄端口且未配置时 fail closed,但 `types.go:3-15` alias 到 bizdev 模型。`internal/app/chatflow/ports.go:75-106` 返回 ctxmgr/coordinator/projectinstance 具体类型。接口改变了调用位置,仍未彻底隐藏内部模型。
|
||||
|
||||
本次 server 生产 SQL 搜索未发现真实业务 SQL,**不能把持有 pool 等同于已经直接越权写表**。改造重点是缩小可用能力与未来变更影响,不虚构违规。
|
||||
|
||||
## B2. 单写者有登记,仍有明确例外
|
||||
|
||||
`.doc/system-design/module-boundaries.yaml:276-374` 登记表 owner,`:471-511` 登记 5 项 SQL 例外。
|
||||
|
||||
源码确认:`internal/docrevision/pg_store.go:456-477` 更新 freeform_documents/document_records;`internal/docduty/store.go:144`、`revoke.go:154` 修改 document_records;`internal/projectinstance/store.go:920` 更新 task_runs。
|
||||
|
||||
建议通过 owner repository/用例端口逐项归还写权,保留必需事务边界。不得为了删除一个 import,把同一 SQL 移到万能公共 repository。
|
||||
|
||||
## B3. 共享事务值得保留,不能随意替换为网络调用
|
||||
|
||||
`internal/app/docflow/commands.go:88-112` 使用 UoW 与 owner GetForUpdate;`internal/docrevision/pg_store.go:482-491` 加入同一事务;`internal/app/uow/pg_uow.go:52-80,95-124` 提供有界事务重试、锁等待和提交。
|
||||
|
||||
`internal/app/app.go:269-275` 接入 events.PGSink,`internal/events/pg.go:17-58` 拒绝无 UoW 发布并同事务创建 envelope/delivery。这是实质强一致性能力。若以后拆进程,必须重新定义跨域中间态、补偿和对账,不能用分布式锁替代事务。
|
||||
|
||||
## B4. 文件与前端状态也构成模块间隐式协议
|
||||
|
||||
文件证据:`internal/coordinator/context.go:456-459` 读取 output.md;`engine.go:691-701,895,1051` 读写/拼接共享产物路径;`internal/docgov/lease.go:415-416` 写 projection 文件。文件 owner、不可变引用、版本与发布原子性应与数据库同样治理。
|
||||
|
||||
前端证据:`web/src/App.tsx:130-170,1163-1204,1367-1406` 将消息、交付物、缓存及 setter 传给多个功能 hooks;`useSendMessage.ts:85-121` 可修改多个域状态。建议 owner 管理状态、对外命令/查询/事件;浏览器内无需强制 HTTP。
|
||||
|
||||
`web/src/api/authHeaders.ts:27-44` 已有认证辅助,但 chatStream、taskRuns、docgov 等仍各自 fetch。统一 client 应处理通用超时、错误、取消与身份,功能 DTO 继续分域。
|
||||
|
||||
## B5. 静态架构检查有价值,但证明范围有限
|
||||
|
||||
`internal/archtest/scan.go:38-70` 默认只扫 internal 生产 Go;`:169-205` 检查 import allowlist 和登记表的 SQL 字符串。它不涵盖前端、默认 cmd 范围、动态 SQL 完整语义、实际权限、兼容性和资源隔离。
|
||||
|
||||
`scan.go:18,392-410` 的事件匹配范围主要为 document.* / intent.plan.* 和特定调用模式,不能根据 YAML 中存在 taskrun/fav 就假定每条协议都被检查。
|
||||
|
||||
`module-boundaries.yaml:182-225` 仍允许 server 依赖大量领域;`:641-651` 登记 sql_write=5、path=16 基线。**这是读取到的配置,不是本次 arch-check 执行结果。**
|
||||
|
||||
---
|
||||
|
||||
## 可保留的隔离与演进能力
|
||||
|
||||
- 独立 worker:`cmd/events/main.go:68-89,127-130` 注册 notify/advance/projection,FAV 明确不注册;Compose 已有 events 服务。历史“没有独立 worker”的判断已过时。
|
||||
- 领取与确认:`dispatcher.go:49-97,163-200,227-259` 有原子 claim、owner/token、attempt、inbox/checkpoint;旧 ClaimIDs 已禁用。
|
||||
- DB 效果原子性:`worker.go:247-275` 的 handler、fenced ack、commit 同事务;仅适用于参与同一 DB 事务的效果,不能扩展为任意外部 IO。
|
||||
- 消费者实现:`internal/app/docproject/consumer.go:73-94` 使用事件回执与 no-replace;`docadvance/consumer.go:80-104` 使用同事务推进与通知。
|
||||
- 冻结执行输入:`internal/workflow/runtime/orchestrator.go:27-41,69-85` 从冻结实例取职责,缺失时拒绝,可泛化为任务绑定配置/契约版本。
|
||||
- PI Runner:`internal/agent/piagent.go:145-194` 有 HTTP/Bearer、超时、run_id、typed result 和大小限制;`remote_result.go:63-79` 对存在但非法 typed 字段 fail closed。
|
||||
|
||||
## 测试证据边界
|
||||
|
||||
以下是已有测试定义,不是本次 PASS:
|
||||
|
||||
| 路径 | 已有测试意图 | 本次状态 |
|
||||
|---|---|---|
|
||||
| internal/app/docflow/same_tx_pg_test.go | 状态与 outbox 同事务回滚 | 未运行 |
|
||||
| internal/events/dispatcher_pg_test.go | 多 worker、off/shadow、ack、隔离 | 未运行 |
|
||||
| internal/app/docnotify/e1a_pg_test.go | 通知权限、shadow/live、回滚 | 未运行 |
|
||||
| internal/app/docadvance/e1b_pg_test.go | 工作流事件消费 | 未运行 |
|
||||
| internal/app/docproject/e1c_pg_test.go | 投影与 no-replace | 未运行 |
|
||||
| internal/agent/piagent_typed_e2e_test.go | 条件启用的真实 PI | 未运行 |
|
||||
|
||||
PG 测试在 short 或缺少独立 DSN 时可跳过;PI 测试有显式开启条件。建议新增 A1–A5 的针对性场景,再做受影响用户入口验收。未经授权,本次不启动这些实现或共享环境测试。
|
||||
|
||||
## 与原方案的关系
|
||||
|
||||
继续引用 2026-09-20 A–G 分批整改作为现有依赖收敛清单,不能据此宣称原批次已经完成。A1–A5 是本次新增或细化的源码证据,实施时应并入同一治理链,避免开第二套互相冲突的解耦项目。
|
||||
|
||||
原规范保留模块化单体,本次通用规范允许按条件选择进程/服务隔离。两者分别用于“当前项目生效规则”和“未来项目待采纳标准”,没有自动覆盖关系。
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
| owner | task | branch | worktree | mutex | status |
|
||||
|---|---|---|---|---|---|
|
||||
| Codex | modular-contract-standard-2026-09-22 | feature/modular-contract-standard-2026-09-22 | .worktrees/modular-contract-standard-2026-09-22 | modular-contract-docs | done |
|
||||
| Codex | herdr-orchestration-v12 | feature/herdr-orchestration-v12 | .worktrees/herdr-orchestration-v12 | herdr-policy+scripts | done |
|
||||
| Codex | 2026-09-21-agent-governance-implementation | feature/agent-governance-implementation | .worktrees/agent-governance-implementation | agent-governance | done |
|
||||
| Cursor | 2026-09-21-force-material-chunk-index | feature/force-material-chunk-index-2026-09-21 | .worktrees/force-material-chunk-index-2026-09-21 | bizdev+materialindex | done |
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: "当前解耦审查与跨项目模块契约规范"
|
||||
task_id: modular-contract-standard-2026-09-22
|
||||
status: done
|
||||
created: 2026-09-22
|
||||
updated: 2026-09-22
|
||||
owner: Codex
|
||||
task_type: research
|
||||
task_tier: multi_stage
|
||||
policy_revision: "agent-governance-2026-09-21-r1 @ d034aa00e5785f51234b484f9dca6bf4fb037660"
|
||||
branch: feature/modular-contract-standard-2026-09-22
|
||||
worktree: .worktrees/modular-contract-standard-2026-09-22
|
||||
base_dev_sha: d034aa00e5785f51234b484f9dca6bf4fb037660
|
||||
write_scope: [".doc/ops/short-term/2026-09-22-modular-contract-standard.md", ".doc/audit/2026-09-22-module-decoupling-current-state.md", ".doc/system-design/2026-09-22-generic-module-contract-standard.md", ".doc/remediation/2026-09-22-modular-platform-adoption-plan.md", ".doc/ops/CURRENT.md"]
|
||||
allowed_environments: []
|
||||
mutex: "path:本任务四份文档;CURRENT 使用行级 CAS"
|
||||
review_state: approved
|
||||
integration_state: pending
|
||||
deployment: not_required
|
||||
cleanup: retained-with-reason
|
||||
cleanup_owner: 独立维护者
|
||||
cleanup_trigger: 本地 dev 集成完成且 owner 退出后由独立维护者复查
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# 当前解耦审查与跨项目模块契约规范
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
2026-09-22 用户要求:“梳理当前系统,看系统的多模块、功能之间的解耦做的如何,并制定规范(和本项目无关的通用规范)”,重点为统一封装、HTTP/Socket/IPC/RPC 通信、接口契约、总线调度、并发数据完整性和幂等,并形成设计规划。交付终点是可审研究与规范草案。允许隔离任务区写文档、只读源码与官方资料检索;不实施业务代码、不部署、不操作数据库、不 push。
|
||||
|
||||
本次是跨项目标准的新研究任务,引用 2026-09-20 项目专用审查作为输入,不代替原 A–G 整改任务、不改变现行 ADR。用户 AGENTS 指定的 .doc taxonomy 优先于技能默认 docs/plans 目录。共享主目录原有三项未跟踪 promotion receipts 保留。
|
||||
|
||||
## 产物与步骤
|
||||
|
||||
1. 当前源码证据:[解耦现状](../../audit/2026-09-22-module-decoupling-current-state.md)。
|
||||
2. 可独立复用的规范:[通用模块契约规范](../../system-design/2026-09-22-generic-module-contract-standard.md)。
|
||||
3. 平台封装与渐进采用:[落地方案](../../remediation/2026-09-22-modular-platform-adoption-plan.md)。
|
||||
4. 文档交叉审阅、路径与差异检查;本记录承载验证和交接。
|
||||
|
||||
## 基线与验证计划
|
||||
|
||||
- 本地 dev 完整 SHA 见 frontmatter;创建时 origin/dev...dev 为 0 / 6,仅本地 remote-tracking 比较,未 fetch,不能作为远端实时状态。
|
||||
- 两个只读子审查共用父任务:coupling_evidence(模块边界),bus_evidence(事件与事务),不独立写入。
|
||||
- 实际环境未验收;本次只做源码证据与文档验证,不运行 Go/PG/PI/浏览器测试。
|
||||
- 不更新 FEATURE-MATRIX/gap-matrix:本次不改变模块能力与发布 blocker 声明。
|
||||
- 检索记忆仅用于导航与流程提醒;现状结论须回到当前源码核实。
|
||||
|
||||
## 验证与收口
|
||||
|
||||
本次研究交付完成;用户后续明确要求“合入 dev”,review_state=approved 仅表示同意文档入库,规范仍为 proposed,不自动启用为项目强制规则。用户后续可直接围绕同一 task_id 审阅/修订;业务实施另立授权任务。
|
||||
|
||||
### 实际检查
|
||||
|
||||
| 范围 | 实际检查 | 结果及限制 |
|
||||
|---|---|---|
|
||||
| 固定源码 | 两个只读子审查 + 主审核对 dispatcher、producer、worker 与 ADR | 现状报告保留路径及行号;不等于运行验收 |
|
||||
| 文档链接 | Python 逐项解析四文档本地 Markdown 链接 | 8 条本地链接存在,0 错误 |
|
||||
| 文档结构 | Python 检查 fenced block、行尾空白、末尾换行、路径可移植性 | 0 错误;Mermaid 结构人工检查,未调用渲染器 |
|
||||
| 稳定 ID | Python 检查规范 MCS-01–22 与计划 U1–U8 | 唯一且完整 |
|
||||
| 差异 | git diff --check;git diff -- .doc/ops/CURRENT.md | 无空白错误;CURRENT 仅本任务行,使用 CAS 收据 |
|
||||
| 运行与业务 | Go/arch-check/PG/PI/浏览器/部署 | 本次不适用,未执行;不报告产品 PASS |
|
||||
| 主目录 | git rev-parse dev 与 git status --short | dev 仍为基线 SHA,原三项未跟踪 promotion receipts 保留 |
|
||||
|
||||
所有新增文件也由 Python 检查,避免 git diff --check 不覆盖 untracked 的盲区。只保存源码定位、文档检查结果与公开官方资料链接,不保存业务正文、秘密或环境值。
|
||||
|
||||
### 文档审阅记录
|
||||
|
||||
使用 ce-plan 与 ce-doc-review 的非交互文档审阅流程,三位当前会话子审阅者各承担两种视角;同一审阅者内部的两个视角不计为独立印证。六种视角为 coherence、feasibility、security、adversarial、product、scope。未运行外部 CLI 跨模型复核,不以此作为验证证据。
|
||||
|
||||
| 原始 finding 与证据 | 主审判定及实际修正 | 复核 |
|
||||
|---|---|---|
|
||||
| feasibility,P2/75:U2 仅依赖 U1,却要求持久结果查询;持久收据归 U3 | 接受。U2 区分开发与最终验收依赖;U3 明确单次命令收据及授权查询,U5 管多步流程 | 原审阅者局部只读确认已解决 |
|
||||
| security,P2/75:不可信插件只有进程/资源隔离,不能阻止同 UID 访问宿主秘密 | 接受。MCS-01 后补最小权限、文件/网络限制和不继承秘密;U5 增访问拒绝负例 | 原审阅者局部只读确认已解决 |
|
||||
| scope,P2/75:所有系统统一要求数据库/队列等验收会强加无用设施 | 接受。规范 §8.1 按能力适用,区分 N/A 与 SKIP;U1/U6 同步 manifest 与门禁 | 原审阅者局部只读确认已解决 |
|
||||
| coherence / adversarial / product | 未返回达到行动门槛的 finding | 无需修改 |
|
||||
|
||||
审阅结果:applied=3,proposed_fixes=0,decisions=0,fyi=0。主审另补规范 §2.2 功能设计卡与前端状态边界,与用户“功能、模块区分”一致,经局部复核未发现矛盾。实施前的仓库/owner/试点/SLO 仍须补齐,属于方案明确的后续立项条件,不伪装为已完成。
|
||||
|
||||
### 产物身份
|
||||
|
||||
| 文档 | SHA-256 |
|
||||
|---|---|
|
||||
| audit/2026-09-22-module-decoupling-current-state.md | d73bb9fddc0d88d4848dc711b67fdaac52b9d9383bf7c1938e9ed26f46ca8187 |
|
||||
| system-design/2026-09-22-generic-module-contract-standard.md | a47e4e1a59a31c350ffae936602116f3e944e011d005e7e6237ac11d06b74c19 |
|
||||
| remediation/2026-09-22-modular-platform-adoption-plan.md | 5d4f7c7bcd03e8510f07661f4f05a5adaf974145b1667ae1d70a31d02c770609 |
|
||||
|
||||
### 交接
|
||||
|
||||
- 影响:新增三份研究/规范/规划文档与本任务记录;CURRENT 仅任务 worktree 内本任务认领行;不改业务、原生效规范、FEATURE-MATRIX 或 gap-matrix。
|
||||
- 本地保存:研究完成时文档未提交;后续用户授权本地 dev 集成,执行状态与证据见下节。
|
||||
- 部署:not_required;运行制品未检查、未修改。
|
||||
- 远端:not_requested,未 fetch/push;创建时 local tracking 比较 0/6,不能代表实时远端。
|
||||
- 清理:retained-with-reason。保留任务与集成 worktree,待集成完成且 owner 退出后由独立维护者核对,不满足门禁不得删除。
|
||||
- 后续实施必须重新核验 dev 与生效规则;通用平台线和当前项目 CP1–CP6 分开立项,不能把本次“研究完成”理解为实施授权。
|
||||
|
||||
## 2026-09-22 本地 dev 集成续办
|
||||
|
||||
授权来源:用户后续指令“合入 dev”。交付扩展为本地提交、独立候选验证、受控推进 dev;不 push、不部署、不实施方案。基线仍为 d034aa00e5785f51234b484f9dca6bf4fb037660,已重读相同生效规则,无规则差异。
|
||||
|
||||
逻辑 task_id 沿用原值;原机器登记已关闭且工具不支持 reopen,使用执行登记 modular-contract-standard-2026-09-22-integration-prep,范围仍为原五份文档。为接续原任务 CURRENT 行,登记 docs:admin 但仅对该行使用 CAS,不改布局/其他任务/集成槽位。原 CURRENT 内容已保存在本机临时备份,核实相对 HEAD 只有本任务一行后重新发布取得续办收据。
|
||||
|
||||
候选执行登记 integrate-modular-contract-standard-2026-09-22,分支 feature/integrate-modular-contract-standard-2026-09-22,同名 .worktrees 路径,scope 为本任务五文档,能力仅 git:dev。从上述确切 dev 创建 --no-ff 候选;冻结 SHA 由 Git merge 父节点及 agentctl validate/integrate 收据记录。验证为文件白名单、相对链接、ID、内容哈希、CURRENT 单行差异与 git diff --check;普通文档无须 Go/PI 或容器重建。
|
||||
|
||||
集成前障碍:主目录保留三项非本任务未跟踪 promotion-receipts,clean_source 对集成不适用构建证据例外。按 AGENTS 不擅自移动他人未提交文件;先完成可审候选,再处理该障碍,不绕过 hooks 或清洁检查。
|
||||
@@ -0,0 +1,400 @@
|
||||
---
|
||||
title: "通用模块平台封装与渐进采用 - Plan"
|
||||
type: docs
|
||||
date: 2026-09-22
|
||||
product_contract_source: ce-plan-bootstrap
|
||||
execution: knowledge-work
|
||||
---
|
||||
|
||||
# 通用模块平台封装与渐进采用 - Plan
|
||||
|
||||
## Goal Capsule
|
||||
|
||||
让未来项目在新增功能和处理故障时,能够围绕明确的模块职责、通信契约与数据 owner 独立工作,并通过可重复验证判断问题是否被限制在约定范围内。
|
||||
|
||||
采用[通用模块契约规范 MCS v0.1](../system-design/2026-09-22-generic-module-contract-standard.md),以最小参考实现和故障验收推动平台封装。当前授权仅为现状研究、规范和方案编制;本文后续开发单元是建议工作,不是已启动的实施。
|
||||
|
||||
当前项目证据来自[解耦现状审查](../audit/2026-09-22-module-decoupling-current-state.md)。通用规范独立复用,不依赖本项目的业务对象、路径或运行工具。本项目的既有 ADR/开发标准继续生效。
|
||||
|
||||
---
|
||||
|
||||
## Product Contract
|
||||
|
||||
### Problem Frame
|
||||
|
||||
模块分目录、共用接口和使用消息总线后,仍可能因共享数据、入口旁路、类型泄漏、慢消费者和不一致的失败语义发生连带问题。全面网络化又会增加超时、重复执行、部署和一致性成本。因此需要“职责与契约强约束 + 分档运行隔离 + 可证明的一致性”,并把横切能力封装复用。
|
||||
|
||||
### Requirements
|
||||
|
||||
**边界与复用**
|
||||
|
||||
- R1. 通用标准必须可用于不同语言和业务,项目现状证据独立存放。
|
||||
- R2. 每个模块有职责、公开契约、数据 owner、不变量和目标隔离档位。
|
||||
- R3. 同一业务动作的不同入口保持授权、持久结果与事件语义一致。
|
||||
- R4. 统一封装覆盖共性通信治理,业务规则留在模块和显式编排中。
|
||||
|
||||
**一致性与隔离**
|
||||
|
||||
- R5. 命令、查询、事件、流有不同成功/失败语义;网络未知结果可恢复。
|
||||
- R6. 数据 owner、事务、幂等、顺序与租约共同保证已声明的不变量。
|
||||
- R7. 慢任务、错误、过载和进程崩溃在所选隔离档位下有明确影响范围。
|
||||
- R8. 并发/故障/兼容测试是验收的一部分,不能只靠静态依赖检查。
|
||||
|
||||
**落地范围**
|
||||
|
||||
- R9. 先建立小型可验证参考实现,再推广到第二个场景;不先造完整微服务平台。
|
||||
- R10. 本次只交付待审文档,业务实施、平台仓库创建、外部发布和环境操作留待单独授权。
|
||||
|
||||
### Acceptance Examples
|
||||
|
||||
| ID | 例子 | 验收结果 |
|
||||
|---|---|---|
|
||||
| AE1 | 两个入口提交相同授权动作 | 持久状态与事件一致,拒绝边界相同;覆盖 R3 |
|
||||
| AE2 | 并发重复/响应丢失后重试 | 单一业务效果、结果可查询;覆盖 R5/R6 |
|
||||
| AE3 | 慢消费者 + 其他消费者持续工作 | 后者仍达到约定延迟预算;覆盖 R7 |
|
||||
| AE4 | 首条高版本、乱序和过滤订阅 | 按明确契约推进,不误把空洞当丢失;覆盖 R6 |
|
||||
| AE5 | 新旧契约混跑与失败回退 | 无数据丢失或旧客户端误解释;覆盖 R8 |
|
||||
| AE6 | 参考实现替换传输适配器 | 业务不变量相同,额外网络失败行为显式呈现;覆盖 R4/R5 |
|
||||
|
||||
### Scope Boundaries
|
||||
|
||||
本次文档范围包括:当前实现审查、通用标准、统一封装设计、验证与推广计划。规范本文不指定云厂商、消息产品或全项目微服务化。
|
||||
|
||||
后续工作建议分两条线:
|
||||
|
||||
- **通用平台线**:模板、契约与参考 SDK/适配器/可靠投递套件,放在未来独立模板或平台仓库。
|
||||
- **当前项目线**:优先修复已核实的契约偏差与旁路,再按原 A–G 计划收敛依赖。以单独任务认领,继续遵守项目 ADR。
|
||||
|
||||
跨区高可用、全部语言 SDK、通用可视化流程平台、service mesh、复杂 broker 拓扑均延后到实际需求证明价值。它们不是第一版完成条件。
|
||||
|
||||
---
|
||||
|
||||
## Planning Contract
|
||||
|
||||
### Key Technical Decisions
|
||||
|
||||
- KTD1. **以契约和数据 owner 划分模块,以资源/信任要求划分进程。** 实现 R2/R7;简单业务默认 L1,必须隔离 OOM、非可信插件或重计算选 L2 以上。既不强制每功能一个服务,也不禁止合理拆分。
|
||||
- KTD2. **统一治理入口采用薄 SDK 与适配器,事件采用持久总线。** 实现 R4/R5;Query/同步 Command 可直接访问提供方,经过同样的策略中间件。总线不做所有调用的中心转发服务器,避免放大共享故障点。
|
||||
- KTD3. **长任务调度归持久业务编排器。** 实现 R4/R6;平台提供任务/lease/receipt 原语,流程步骤、补偿和领域规则由用例定义。
|
||||
- KTD4. **一致性分成本地原子事务与跨域恢复协议。** 实现 R6;共库 UoW 和 outbox 是一种部署模式,跨进程使用 outbox/inbox 与 Saga。不能用一个“透明 RPC”承诺两者等价。
|
||||
- KTD5. **可靠投递默认至少一次,幂等由效果 owner 完成。** 实现 R5/R6;借鉴官方 outbox 与幂等 API 原理,具体规则以 MCS-11/13 为准。外部效果的未知结果走查询对账。
|
||||
- KTD6. **业务版本与消息序列分开。** 实现 R6;现状 A1 已显示混用风险,按 MCS-15 显式选择快照、可交换、严格增量三种语义。
|
||||
- KTD7. **第一版只做一套最小组合。** 实现 R9;建议 Go 参考 SDK + 进程内 + HTTP/JSON + PostgreSQL durable delivery。理由是足以验证类型、跨进程失败、事务和恢复,也可复用现有源码经验。该选择只作用于参考实现,规范不要求所有项目使用 Go/PG。
|
||||
- KTD8. **门禁分层并报告未覆盖项。** 实现 R8;静态边界、schema 兼容、真实数据库并发、真实传输故障和运行隔离各自提供证据,不用单个 arch-check 宣称全覆盖。
|
||||
|
||||
选择 HTTP/JSON 是第一版的可检查性与通用接入选择,不是性能判断。gRPC/UDS 在强类型多语言或本机进程需求出现后增加;WebSocket 留给双向流;SSE 面向单向 UI 通知。每次增加适配器都跑相同业务契约和该传输特有故障测试。
|
||||
|
||||
### Alternatives
|
||||
|
||||
| 方案 | 收益 | 代价 | 决策 |
|
||||
|---|---|---|---|
|
||||
| 所有功能独立 HTTP 服务 | 独立进程/发布机会 | 同步链、运维和事务复杂性高 | 只用于已证明需要独立隔离的模块 |
|
||||
| 所有调用经过中央 bus 服务 | 集中路由与监控 | 单点、语义弱化、延迟和共享瓶颈 | 不采用为通用默认 |
|
||||
| 只用进程内接口 | 简洁、高效、本地事务 | 无崩溃/资源隔离 | 作为 L1 档 |
|
||||
| 显式契约 + 可替换适配器 + 持久事件 | 按风险演进、可验证 | 需要契约治理和一致性测试 | 推荐 |
|
||||
| 立即部署复杂消息集群 | 扩展能力丰富 | 缺容量证据、增加运营成本 | 达到触发条件后评估 |
|
||||
|
||||
消息基础设施第一版可用事务型数据库队列。若多服务解耦、积压量、重放规模或独立吞吐突破数据库预算,再比较 NATS JetStream、RabbitMQ、Kafka 或云队列;选择需按顺序语义、retention、运维能力与真实 benchmark,而非流行度。即使 broker 提供去重,也仍须 MCS-13 的业务收据。
|
||||
|
||||
### High-Level Technical Design
|
||||
|
||||
系统组件和持久交付时序以规范 §3、§6 两张图为准。平台发布时按能力拆包,业务模块只依赖最小集合。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Contract["U1 契约与设计卡"] --> SDK["U2 调用治理与适配器"]
|
||||
Contract --> Data["U3 本地事务与幂等"]
|
||||
SDK --> Event["U4 可靠事件消费"]
|
||||
Data --> Event
|
||||
Event --> Schedule["U5 持久调度与隔离"]
|
||||
Contract --> Gates["U6 自动门禁"]
|
||||
SDK --> Gates
|
||||
Event --> Gates
|
||||
Schedule --> Gates
|
||||
Gates --> Pilot["U7 第二场景试点"]
|
||||
Pilot --> Release["U8 发布与推广"]
|
||||
```
|
||||
|
||||
### Output Structure
|
||||
|
||||
下面是**未来独立参考仓库**的建议相对路径,不是本仓库将要新增的业务文件,也不是对已存在文件的声明:
|
||||
|
||||
```text
|
||||
spec/
|
||||
module.schema.json
|
||||
operation.schema.json
|
||||
errors.yaml
|
||||
compatibility.md
|
||||
templates/
|
||||
module-card.md
|
||||
contract.md
|
||||
failure-model.md
|
||||
sdk/go/
|
||||
contract/
|
||||
invoke/
|
||||
transport/inprocess/
|
||||
transport/http/
|
||||
persistence/postgres/
|
||||
delivery/
|
||||
scheduler/
|
||||
conformance/
|
||||
fixtures/
|
||||
contract_test.go
|
||||
concurrency_test.go
|
||||
delivery_test.go
|
||||
isolation_test.go
|
||||
examples/
|
||||
reservation/
|
||||
projection/
|
||||
webhook/
|
||||
ci/
|
||||
boundary-rules.yaml
|
||||
runbooks/
|
||||
recovery.md
|
||||
release.md
|
||||
```
|
||||
|
||||
样例使用“资源预留 → 查询投影”展示可恢复工作流,无真实付费或外部通知副作用。外部效果另用可查询、支持幂等的本地 webhook 接收进程验证故障窗口。示例不使用本项目的文档审批等术语。
|
||||
|
||||
### Assumptions and Deferred Decisions
|
||||
|
||||
- 文中的“数据库和所”按“数据库和锁”理解;规范已区分唯一约束、事务、CAS、锁和 lease 的作用。
|
||||
- 新项目的业务规模、语言和运维环境尚未指定,当前不承诺吞吐、时限和工程工期。
|
||||
- 参考语言 Go 是建议起点,可在实施立项时替换;稳定 IDL、测试向量和语义仍然保留。
|
||||
- 未来平台仓库路径/owner、首个试点系统、业务 SLO 和外部副作用种类是**实施启动前必填项**。不阻塞本次方案交付,但没有这些信息不得称平台已经实施就绪或直接开工。
|
||||
|
||||
---
|
||||
|
||||
## Implementation Units
|
||||
|
||||
所有 U 单元均为后续建议。每单元以独立可验收结果收口,代码与其必要回归测试同批提交,避免先搭完框架再验证。
|
||||
|
||||
### U1. 契约与模块设计模板
|
||||
|
||||
**目标/需求**:实现 R1/R2/R5。把规范落实为模板与可校验 manifest,先明确人读语义与机器字段的对应关系。
|
||||
|
||||
**依赖**:实施前选择试点与 owner。
|
||||
|
||||
**文件**:spec/module.schema.json、spec/operation.schema.json、spec/errors.yaml、spec/compatibility.md、templates/ 三份模板、conformance/contract_test.go。
|
||||
|
||||
**做法**:模板覆盖 MCS-02/08,manifest 声明能力及 N/A 理由,不生成业务方法大全。契约给出资源预留命令、预留状态查询、预留完成事件三个相互关联样例;事件信封版本与 payload 版本分开。OpenAPI 与事件 schema 来源单一。
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. 缺 owner/数据边界/错误语义的 manifest 拒绝。
|
||||
2. 未知 major、字段改类型、枚举扩展按兼容策略发现差异。
|
||||
3. 新增可选字段时旧消费者仍能处理。
|
||||
4. Contract DTO 不引入业务实现/数据库类型。
|
||||
|
||||
**验收**:新开发者仅依据设计卡能判断谁可调用、写什么、失败怎么恢复;schema 工具只能证明的部分在报告中标出。
|
||||
|
||||
### U2. 统一调用治理与两种适配器
|
||||
|
||||
**目标/需求**:实现 R3/R4/R5,覆盖 AE1/AE6。
|
||||
|
||||
**依赖**:开发依赖 U1,可与 U3 并行;涉及 committed/unknown 持久结果查询的最终验收依赖 U3。
|
||||
|
||||
**文件**:sdk/go/contract/、invoke/、transport/inprocess/、transport/http/;conformance/contract_test.go,新增 conformance/transport_test.go。
|
||||
|
||||
**做法**:显式注入类型化客户端;统一身份、deadline、错误、trace、大小限制和取消。进程内与 HTTP 共享业务契约,HTTP 单独暴露断连/未知结果,不模拟成无失败本地函数。
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. 两入口相同输入产生相同业务结果及拒绝边界。
|
||||
2. 下游超时,剩余预算向下传播,不叠加完整超时。
|
||||
3. 超大请求、未知字段策略、伪造租户和错误 envelope 均按契约处理。
|
||||
4. HTTP 提交后响应中断,返回/记录 unknown 并能按 operation 查询。
|
||||
5. 客户端取消后已提交结果不被假称回滚。
|
||||
|
||||
**验收**:替换适配器不修改业务不变量测试;业务入口不依赖整个 Runtime 或公共万能 Store。
|
||||
|
||||
### U3. 单写者、事务和幂等收据
|
||||
|
||||
**目标/需求**:实现 R6,覆盖 AE2。
|
||||
|
||||
**依赖**:U1;可与 U2 并行开发,集成时统一契约。
|
||||
|
||||
**文件**:sdk/go/persistence/postgres/;examples/reservation/;conformance/concurrency_test.go,新增 conformance/idempotency_test.go。
|
||||
|
||||
**做法**:以唯一约束、expected_version 和同事务结果收据维护预留不变量;UoW 不穿越网络。无副作用的领域计算与提交校验分开。提供单次命令结果收据及按幂等键/operation_id 的授权查询;U5 在其上管理多步骤流程状态。
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. 同键并发 100 次,仅一个效果且所有成功重放得到等价结果。
|
||||
2. 同键异摘要冲突;不同键抢同版本只有一个成功。
|
||||
3. 数据变更与结果收据中途失败一起回滚。
|
||||
4. 序列化失败重跑完整事务,但不重放事务外副作用。
|
||||
5. 收据到期、迟到重试与业务唯一号保护符合契约。
|
||||
|
||||
**验收**:真实独立数据库下验证并发与回滚;每种表/资源只有一个 owner,不以进程 mutex 验收。
|
||||
|
||||
### U4. Outbox、消费者收据与顺序策略
|
||||
|
||||
**目标/需求**:实现 R5/R6/R8,覆盖 AE2/AE4。
|
||||
|
||||
**依赖**:U2/U3。
|
||||
|
||||
**文件**:sdk/go/delivery/、examples/projection/、examples/webhook/;conformance/delivery_test.go,新增 conformance/recovery_test.go。
|
||||
|
||||
**做法**:按 MCS-11/13/14/15 实现持久投递、按订阅者状态、短事务认领、token 校验、失败分类和受控重放。发布与消费入口均验证 schema/来源/租户;去重比较摘要。
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. DB commit 后 relay 崩溃,重启仍交付;事务回滚不交付。
|
||||
2. 效果提交后丢 ack,重复投递不重复效果。
|
||||
3. 首条版本 42、42→47、重复/乱序、部分订阅按所选策略工作。
|
||||
4. 同事件 ID 异 payload 被拒绝;错误生产身份/未知 schema 隔离。
|
||||
5. worker 旧 token ack 和旧版本写入均拒绝。
|
||||
6. webhook 已完成但回执丢失,经同键/查询恢复,不能再次执行。
|
||||
7. 重放超出效果收据保留窗口,系统拒绝或走明确恢复流程。
|
||||
|
||||
**验收**:真实独立进程、数据库与传输故障证据齐全;不能以 MemoryBus 通过代替本单元。
|
||||
|
||||
### U5. 持久调度与资源隔离
|
||||
|
||||
**目标/需求**:实现 R4/R7,覆盖 AE3。
|
||||
|
||||
**依赖**:U4。
|
||||
|
||||
**文件**:sdk/go/scheduler/、sdk/go/delivery/;conformance/isolation_test.go,新增 conformance/scheduler_test.go;runbooks/recovery.md。
|
||||
|
||||
**做法**:为两步预留/投影流程保存 operation/step 状态;有界并发、独立消费者执行槽、公平调度、停止领取与排空。高风险执行器进独立进程。业务补偿由示例模块显式提供。
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. 一个消费者阻塞,其他消费者达到试点声明的延迟预算。
|
||||
2. 预取任务尚未执行就租约过期,不导致未受控并发效果。
|
||||
3. 任务运行中进程终止,另一个 worker 接管;旧 worker 恢复后不能写入。
|
||||
4. 重复定时触发、重复取消、补偿失败均有稳定持久状态。
|
||||
5. 过载有背压且 accepted 只用于已可靠保存的工作。
|
||||
6. 停机排空超时后任务可恢复,进程 liveness 与业务进度分别反映。
|
||||
7. 不可信执行器无法读取宿主凭证、访问未授权业务路径或直连未授权数据服务;测试使用合成秘密与隔离数据。
|
||||
|
||||
**验收**:记录故障域、负载和指标;L1 仅证明逻辑隔离,L2 场景必须杀实际子进程验证。
|
||||
|
||||
### U6. 架构与契约门禁
|
||||
|
||||
**目标/需求**:实现 R2/R8,覆盖 AE5。
|
||||
|
||||
**依赖**:U1;最终验收依赖 U2–U5。
|
||||
|
||||
**文件**:ci/boundary-rules.yaml、spec/compatibility.md、conformance/fixtures/;新增 conformance/boundary_test.go 和 conformance/compatibility_test.go。
|
||||
|
||||
**做法**:依赖方向、内部类型引用、数据 owner、schema diff、消费者兼容与真实故障套件分开报告。语言 AST 负责静态边界,数据库 role 负例验证运行权限;不能仅正则扫描就宣称阻止所有动态 SQL。
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. 故意引入内部实现依赖和模块环,门禁失败。
|
||||
2. 越 owner 写表的普通运行身份被数据库拒绝。
|
||||
3. 扩字段、改类型、旧消费者枚举不兼容给出明确结果。
|
||||
4. 例外过期、基线增加、未执行关键测试不能生成全绿报告;经能力核对的 N/A 与适用但未执行的 SKIP 分开。
|
||||
|
||||
**验收**:每项 enforced 有正负例与工具版本;提供静态/运行未覆盖说明。
|
||||
|
||||
### U7. 第二业务场景与采用验证
|
||||
|
||||
**目标/需求**:实现 R1/R9。
|
||||
|
||||
**依赖**:U6。
|
||||
|
||||
**文件**:在未来试点仓库新增模块设计卡、契约和 conformance 映射;参考仓库新增 examples/second-domain/ 与 conformance/adoption_test.go。确切业务路径待试点确定后补充,当前不假定存在。
|
||||
|
||||
**做法**:选择与资源预留不同的真实场景,记录 SDK 引入的代码量、学习成本、业务接口数量、部署需求与故障定位耗时。优先复用原语,不复制第一个业务模型。
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. 第二场景无需 import 第一个示例的实现。
|
||||
2. 同样的重复、取消、失败恢复测试向量能够复用。
|
||||
3. 必须增加的扩展点有两个实际调用场景,避免预先制造万能框架。
|
||||
|
||||
**验收**:证明封装具有跨场景复用价值;否则收缩平台表面并改模板,不能直接全项目推广。
|
||||
|
||||
### U8. 版本发布与渐进推广
|
||||
|
||||
**目标/需求**:实现 R8/R9。
|
||||
|
||||
**依赖**:U7。
|
||||
|
||||
**文件**:runbooks/release.md、runbooks/recovery.md、spec/compatibility.md;新增 conformance/upgrade_test.go。
|
||||
|
||||
**做法**:按版本发布契约、SDK、适配器和测试向量,声明支持矩阵。迁移一条纵向能力后再推进下一条;采用 expand/contract,不双写两套权威状态。
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. 新旧 SDK/服务混跑可处理兼容请求与积压旧事件。
|
||||
2. 业务提交后回退程序版本,状态与事件仍可读取,不删收据制造重放。
|
||||
3. 从备份恢复后,外部不可逆副作用通过对账处理。
|
||||
4. 操作人员可按手册处理 DLQ、unknown operation 和失效租约。
|
||||
|
||||
**验收**:版本支持期、升级/回退与恢复责任明确,完成的试点才进入推广。
|
||||
|
||||
---
|
||||
|
||||
## Verification Contract
|
||||
|
||||
实施验证分五层:纯领域/公开用例、进程内与 HTTP 契约、独立数据库并发、真实进程交付故障、试点用户端到端。每层独立报告,SKIP/未运行不得计 PASS。
|
||||
|
||||
| 单元 | 核心证据 | 失败时处理 |
|
||||
|---|---|---|
|
||||
| U1/U2 | schema/兼容与同一动作不同入口结果 | 修契约或适配器,不改业务含义掩盖差异 |
|
||||
| U3 | 并发竞争、事务与收据回滚 | 停止引入更多消费者 |
|
||||
| U4 | 崩溃/重投/稀疏版本/错误来源 | 保留 delivery 与诊断,不清库重来 |
|
||||
| U5 | 慢任务、过载、旧 worker 复活、排空 | 降低领取/并发,调整隔离,重测受影响预算 |
|
||||
| U6 | 人为违规负例与测试缺项拒绝 | 不标 enforced |
|
||||
| U7/U8 | 第二场景、混跑、恢复与操作手册 | 不扩大推广范围 |
|
||||
|
||||
测试记录必须包含源版本、制品、运行拓扑、配置摘要、预期场景、实际执行、失败/重试和证据位置。容量目标由试点 SLO 确定后固化,不能凭方案给出未经测量的“毫秒级”“零影响”。
|
||||
|
||||
---
|
||||
|
||||
## Current Project Adoption
|
||||
|
||||
这部分是当前项目的映射附录,不属于跨项目通用标准。保持既有模块化单体 ADR,后续如拆进程另提 ADR 和授权。
|
||||
|
||||
| 顺序 | 目标与现有路径 | 建议新增/扩展验证 | 关联 |
|
||||
|---|---|---|---|
|
||||
| CP1 | events/dispatcher.go 对齐首事件 bootstrap;校验所有 producer 的版本语义 | dispatcher_pg_test.go:首条高版本、跳版本、部分订阅;pg_test.go 不再固化矛盾规则 | 审查 A1、MCS-15 |
|
||||
| CP2 | chat/action_executor.go 汇入 app/docflow.Commands;检查所有批准/提交入口 | chat/action_executor_test.go(新增或扩展)、server HTTP/真实对话批准:状态+事件+下游一致 | A2、MCS-04 |
|
||||
| CP3 | events 的发布/消费 schema、producer 与 digest 冲突校验 | events/envelope_test.go、pg_test.go、dispatcher_pg_test.go:非法来源/字段/同ID异摘要 | A3/A5 |
|
||||
| CP4 | events worker 分消费者预算,按容量 claim,补监控 | events/worker_isolation_test.go(计划新增)、真实 PG/多进程慢消费者与 lease 测试 | A4/A5 |
|
||||
| CP5 | 原 A/B/C/D/G 收敛 server 装配、契约类型、workflow 方向与数据/文件 owner | 对应公开用例特征测试与逐项删除例外的静态证据 | 原 A–G 计划 |
|
||||
| CP6 | 原 F 收敛前端 client/状态 owner,再按需做 E 文件拆分 | web API 错误/取消测试、受影响真实页面验收 | B4/B5 |
|
||||
|
||||
CP1–CP4 先验证事实和修复原契约,不以本方案自动修改生效 ADR;各项都需实施任务。CP5 保留原批次 owner 与 mutex,避免同名领域同时两套重构。文件长度用于定位,不以拆文件作为解耦验收。
|
||||
|
||||
项目现有业务与 PG 事务边界尚可受益于 L1 收敛,不建议先做全模块 HTTP 化。优先独立执行器/解析器等高资源或高风险边界时,另补权限、artifact owner、取消和恢复契约。
|
||||
|
||||
---
|
||||
|
||||
## Risks and Operating Model
|
||||
|
||||
| 风险 | 约束/缓解 |
|
||||
|---|---|
|
||||
| 总线成为万能业务引擎 | 按 MCS-07 分开路由、投递、编排和领域逻辑 |
|
||||
| SDK 大版本导致全部模块同时升级 | 最小依赖包、兼容矩阵、协议和实现分版 |
|
||||
| 共享 DB 成为隐式通信 | owner、运行权限与投影,跨域查询例外有期限 |
|
||||
| 锁 TTL 被误认为排他完整证明 | MCS-14 要求资源端拒绝旧写,外部 API 另用幂等/对账 |
|
||||
| 为统一协议消除所有业务差异 | 统一技术语义,业务输入输出分模块定义 |
|
||||
| 推广早于真实恢复证明 | U7 第二场景后才进入 U8 |
|
||||
| 通用规范覆盖本项目现行规则 | 规范保持 proposed,采纳必须有适用范围和生效版本 |
|
||||
| 过度工程影响交付速度 | 第一版限制适配器数量,每个扩展点有实际场景 |
|
||||
|
||||
职责建议:模块 owner 负责不变量与契约;平台 owner 负责 SDK、投递和兼容工具;运维负责容量、故障演练与恢复;集成/评审者核对端到端跨模块行为。小团队可兼任,责任不能空缺。
|
||||
|
||||
不提供缺乏人员与容量输入的固定工期。排期按 U1 → U2/U3 → U4 → U5/U6 → U7 → U8 的依赖推进,各阶段先验收再扩大范围。
|
||||
|
||||
---
|
||||
|
||||
## Definition of Done
|
||||
|
||||
**本次研究交付**:现状报告明确源码证据与未验证范围;通用规范脱离本项目仍完整可读;本方案列明统一封装边界、技术选择、工作单元、故障测试与采用顺序;文档链接、状态与审阅记录可核对。
|
||||
|
||||
**后续平台交付**:U1–U8 对应证据齐全、第二场景验证通过、运维恢复和兼容矩阵可执行。模块故障隔离只按已实测档位声明。
|
||||
|
||||
**当前项目改造交付**:按获授权 CP 任务范围分别验收,不能用本次文档交付代替代码、PG、真实产品 Runner 或浏览器证明。
|
||||
|
||||
## Sources
|
||||
|
||||
机制依据和官方链接已放在通用规范相应条款:gRPC deadline/retry、OpenAPI/AsyncAPI、CloudEvents、PostgreSQL 行锁、AWS outbox/幂等 API。它们支持机制选择,不直接证明本项目运行表现。项目落点与证据来自同批现状报告。
|
||||
|
||||
可能相关的现有文档包括模块开发标准、module-boundaries.yaml、模块化单体 ADR、checkpoint ADR、09-20 A–G 计划及验证契约。只有后续实施真正改变它们的声明时才更新,不在此次方案中预先把能力记完成。
|
||||
@@ -0,0 +1,386 @@
|
||||
---
|
||||
title: "通用模块化架构与通信契约规范"
|
||||
type: docs
|
||||
date: 2026-09-22
|
||||
standard_id: MCS
|
||||
version: "0.1"
|
||||
policy_state: proposed
|
||||
---
|
||||
|
||||
# 通用模块化架构与通信契约规范
|
||||
|
||||
本规范适用于新建或渐进改造的软件系统,独立于具体项目、语言、框架、云厂商和消息产品。目标是让功能变更、故障和数据责任尽可能局限在明确范围内,同时让模块能够独立测试、替换和按需部署。
|
||||
|
||||
这是待审核的通用规范草案。文中的 MUST 表示采纳本规范后必须满足,SHOULD 表示偏离时须有记录,MAY 表示按需选用。规范存在不代表检查工具或运行保证已经实现。
|
||||
|
||||
## 1. 基本决策
|
||||
|
||||
**严格划分职责与契约,按故障风险选择部署边界。** 默认从契约明确的模块化单体起步;重计算、不可信插件、独立扩缩容或必须隔离崩溃的模块使用独立进程或服务。若产品要求任何一个模块崩溃都不能终止其他模块,必须选进程隔离档,不能以进程内接口验收。
|
||||
|
||||
HTTP、RPC 是调用协议;Socket 是通信基础;IPC 是进程间通信方式,三者不是同一层次的互斥选项。gRPC 可以运行在 TCP 或 Unix Domain Socket 上。单独“换成 HTTP”不会消除数据、时序和业务语义耦合。
|
||||
|
||||
### 1.1 名词与粒度
|
||||
|
||||
| 概念 | 定义 | 划分依据 |
|
||||
|---|---|---|
|
||||
| 模块 | 拥有一组业务不变量、公开契约和内部实现的职责单元 | 数据所有权、变化原因、规则归属 |
|
||||
| 功能 | 用户能够完成的一件事 | 使用目标与验收结果 |
|
||||
| 用例 | 完成一个业务动作的应用入口 | 权限、输入、原子性、输出 |
|
||||
| 聚合 | 同一次提交必须维护不变量的数据集合 | 事务一致性要求 |
|
||||
| 服务/进程 | 可独立运行和部署的单位 | 故障、资源、信任和运维边界 |
|
||||
| 适配器 | 将公开语义映射到具体存储或传输的实现 | HTTP、RPC、进程内端口、数据库等 |
|
||||
| 总线 | 提供命令投递或事件分发的基础设施 | 投递语义、背压、资源隔离 |
|
||||
| 编排器 | 根据持久业务状态协调多个用例 | 流程、超时、重试、补偿 |
|
||||
|
||||
一个功能可以组合多个模块;一个模块可以支撑多个功能。SHOULD 按业务能力划分,不能“一张表一个模块”“一个按钮一个服务”。修改一项领域规则必须同时修改多个模块,通常说明边界或公开契约不合理。
|
||||
|
||||
### 1.2 隔离档位
|
||||
|
||||
| 档位 | 实现 | 可提供 | 仍共享的风险 |
|
||||
|---|---|---|---|
|
||||
| L1 逻辑隔离 | 同进程独立包、显式端口、所有权检查 | 编译依赖、责任与测试隔离 | panic/OOM、线程池、地址空间、部署 |
|
||||
| L2 进程隔离 | 本机子进程/容器、IPC/HTTP/RPC、独立资源配额 | 进程崩溃与部分资源隔离 | 主机、磁盘、网络、数据库 |
|
||||
| L3 服务隔离 | 独立部署、身份、容量、存储权限 | 独立发布与扩缩容 | 共享基础设施、链路依赖 |
|
||||
| L4 故障域隔离 | 独立基础设施/区域及复制恢复设计 | 主机/区域级隔离 | 跨域一致性、成本与恢复复杂性 |
|
||||
|
||||
MCS-01:每个模块 MUST 声明目标隔离档位、可承受故障、依赖失效行为及验收方法。不能把 L1 宣称为 L2,也不能承诺绝对“问题不扩散”。
|
||||
|
||||
不可信代码除独立进程与资源配额外,MUST 使用最小权限执行身份、明确的文件与网络访问限制,不继承宿主秘密。普通同用户子进程只能作为崩溃隔离手段,不能作为信任隔离验收;具体沙箱/容器权限以负例验证。
|
||||
|
||||
---
|
||||
|
||||
## 2. 模块边界与开发规则
|
||||
|
||||
MCS-02:模块 MUST 有唯一责任 owner、内部模型、公开契约、数据清单和不变量。共享代码只能承载稳定且无业务行为的基础类型,不能形成公共万能 Store/Manager。
|
||||
|
||||
MCS-03:跨模块调用 MUST 经过公开契约,禁止引用对方内部实现、ORM 实体、数据库事务对象、可变内存指针或内部文件路径。内部包依赖图 MUST 无环;业务流程允许回调和迭代,但须在编排状态机中显式建模。
|
||||
|
||||
MCS-04:同一业务动作的 Web、CLI、消息消费、定时任务和 Agent 工具入口 MUST 汇入相同应用用例,或通过契约测试证明语义一致。各入口不能各自复制授权、状态变更、事件发送。
|
||||
|
||||
MCS-05:模块外部可见契约的语义由提供方负责;进程内调用方 MAY 定义更窄的需求接口,由适配器映射。独立共享契约包与服务 IDL 是发布边界,不能绑定实现包。共享词汇变化也需要兼容审查。
|
||||
|
||||
建议模块内部结构:公开契约、应用用例、领域规则、存储/传输适配器、测试。小模块 MAY 合并文件,层数和目录数量不是验收指标。组合根只负责装配,不藏业务规则;领域层不创建全局客户端或定位服务。
|
||||
|
||||
### 2.1 模块设计卡
|
||||
|
||||
设计前 MUST 填写以下内容;不适用项写原因:
|
||||
|
||||
| 项目 | 必需内容 |
|
||||
|---|---|
|
||||
| 身份 | module_id、owner、目的、非职责、隔离档位 |
|
||||
| 数据 | 唯一写入表/对象/文件命名空间、投影、保留期 |
|
||||
| 不变量 | 哪些状态必须一起提交、竞争对象、合法状态转换 |
|
||||
| 入口 | 命令、查询、事件订阅、流;调用者与权限 |
|
||||
| 输出 | 结果、事件、错误、外部副作用 |
|
||||
| 依赖 | 必需/可选依赖、故障降级、允许依赖方向 |
|
||||
| 并发 | 聚合键、版本比较、唯一约束、锁顺序、并发配额 |
|
||||
| 生命周期 | 启动、readiness、排空、取消、恢复、版本升级 |
|
||||
| 验收 | 契约测试、故障注入、资源隔离与恢复证据 |
|
||||
|
||||
### 2.2 功能设计卡与前端边界
|
||||
|
||||
跨模块功能 SHOULD 另有一张功能设计卡:用户目标、入口、参与模块及 owner、命令/查询/事件时序、授权决策点、原子提交范围、处理中/部分失败/完成的可见结果、补偿与恢复、端到端验收。卡片引用各模块契约,不复制第二套业务规则。
|
||||
|
||||
前端也按功能 owner 管理状态,对外提供查询、命令与事件,避免把可变对象和任意 setter 传遍功能模块。浏览器内模块可以使用本地类型化接口,不必人为绕经网络;缓存刷新、取消、流重连及过期结果处理属于功能契约。
|
||||
|
||||
---
|
||||
|
||||
## 3. 通信架构与统一封装
|
||||
|
||||
统一的是业务契约和治理策略,传输适配器按使用场景提供。不能把所有调用改成字符串 topic 加任意 JSON,从而失去类型、授权和依赖检查。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Entry["Web / CLI / Agent / 定时器"] --> Adapter["入口适配与认证"]
|
||||
Adapter --> Usecase["应用用例或持久流程编排"]
|
||||
Usecase --> PortA["模块 A 公开契约"]
|
||||
Usecase --> PortB["模块 B 公开契约"]
|
||||
PortA --> A["模块 A 内部实现"]
|
||||
PortB --> B["模块 B 内部实现"]
|
||||
A --> StoreA["A 所有的数据与 Outbox"]
|
||||
B --> StoreB["B 所有的数据与 Inbox"]
|
||||
StoreA --> Relay["可靠投递器"]
|
||||
Relay --> Bus["事件总线"]
|
||||
Bus --> Sub["独立消费者队列与执行预算"]
|
||||
Sub --> PortB
|
||||
Policy["契约目录 / 身份 / 追踪 / 配额"] -.-> Adapter
|
||||
Policy -.-> Relay
|
||||
Policy -.-> Sub
|
||||
```
|
||||
|
||||
### 3.1 总线与调度的分工
|
||||
|
||||
MCS-06:平台 MUST 区分四类语义,允许共用基础代码,不得混用成功含义:
|
||||
|
||||
| 机制 | 含义与接收方 | 成功定义 | 必需约束 |
|
||||
|---|---|---|---|
|
||||
| Query 调用 | 一个提供方返回信息 | 得到满足一致性要求的结果 | 超时、读权限、数据新鲜度 |
|
||||
| Command 调用/投递 | 一个逻辑 owner 接受业务动作,可多副本执行 | 同步 committed,或异步 accepted + operation_id | 持久性、幂等、结果查询 |
|
||||
| Event 分发 | 已发生事实,零到多个订阅者 | 发布持久化不等于消费者完成 | outbox、每订阅者独立进度 |
|
||||
| Stream | 有序交互或增量结果 | 通道建立不等于业务提交 | 游标、断线恢复、背压 |
|
||||
|
||||
MCS-07:总线 MUST 只负责技术治理:校验、路由、投递、租约、确认、重试预算、可观测性。它不拥有业务表、不决定业务状态、不持业务全局锁。业务编排器通过契约发命令、消费事实,持久保存流程进度。
|
||||
|
||||
“所有模块必须经过统一通信治理”可以实现为每个进程内的 SDK/代理与分布式 broker;不要求所有 HTTP 查询绕经单个中央服务。控制面故障时,已验证的契约与路由快照可按明确有效期继续使用;权限不可凭过期缓存无限放行。
|
||||
|
||||
### 3.2 传输选择
|
||||
|
||||
| 场景 | 首选 | 使用前必须解决 |
|
||||
|---|---|---|
|
||||
| 同进程、同发布、低风险 | 窄端口与依赖注入 | 不共享可变内部对象;不冒充崩溃隔离 |
|
||||
| 浏览器/第三方调用 | HTTP + OpenAPI | 身份、错误、分页、幂等、版本 |
|
||||
| 服务间强类型调用 | gRPC + Protobuf | deadline、兼容、健康、重试预算 |
|
||||
| 同主机进程隔离 | HTTP/gRPC over UDS 或成熟 IPC | OS 权限、消息边界、断连与重启 |
|
||||
| 浏览器增量单向通知 | SSE | cursor、重连、事件保留与重同步 |
|
||||
| 双向低延迟交互 | WebSocket | 身份续期、帧限制、心跳、序列/确认 |
|
||||
| 可延迟、可重放副作用 | 持久消息队列/事件总线 | ack、重复、顺序、死信、重放权限 |
|
||||
|
||||
不建议第一版自造裸 TCP 帧协议,也不同时实现全部传输。HTTP/gRPC 与事件协议各维护一个权威描述,生成代码只是投影,不能让多套定义互相漂移。
|
||||
|
||||
gRPC 未设置 deadline 可能无限等待;因此所有跨进程调用必须设置并传递剩余预算,不能每一跳重置完整超时。[gRPC deadline 官方说明](https://grpc.io/docs/guides/deadlines/)
|
||||
|
||||
### 3.3 可复用平台包
|
||||
|
||||
| 包/能力 | 负责 | 明确不负责 |
|
||||
|---|---|---|
|
||||
| contract-core | 元数据、错误类别、契约版本、兼容检查 | 业务 DTO 全部塞入公共包 |
|
||||
| transport-* | 编解码、连接、身份、deadline、trace | 状态机、表写入 |
|
||||
| invocation-policy | 配额、并发上限、熔断、有限重试 | 掩盖永久错误、无限重试 |
|
||||
| delivery | outbox、消费状态、租约、DLQ、重放管理 | 通用业务补偿 |
|
||||
| consistency | 幂等收据、CAS 辅助、事务集成扩展点 | 跨库“万能事务” |
|
||||
| lifecycle | readiness、drain、取消、恢复钩子 | 强行吞掉 OOM 或任意运行时错误 |
|
||||
| conformance | 契约兼容、架构依赖、故障测试套件 | 用接口 mock 代替真实投递验收 |
|
||||
|
||||
平台核心不得 import 业务模块。通过显式注册及生成的类型化客户端暴露能力,避免全局 service locator。每种适配器发布能力清单,缺少持久投递能力的内存实现只能用于明确的临时通知或测试。
|
||||
|
||||
---
|
||||
|
||||
## 4. 接口契约
|
||||
|
||||
MCS-08:契约 MUST 同时包含结构、语义、权限、一致性和失败行为,不能只有接口名和字段。
|
||||
|
||||
### 4.1 每个操作的必填项
|
||||
|
||||
| 维度 | 内容 |
|
||||
|---|---|
|
||||
| 标识与版本 | module、operation、schema version、owner、兼容窗口 |
|
||||
| 请求 | 字段类型、长度/大小上限、必填、缺省/null/未知枚举策略 |
|
||||
| 身份 | caller_service、actor/delegation、tenant、resource/action、信任来源 |
|
||||
| 前置条件 | 资源状态、expected_version、前置业务约束 |
|
||||
| 执行语义 | command/query/event/stream、同步/异步、幂等范围 |
|
||||
| 一致性 | 原子提交范围、读取陈旧度、读己之写条件 |
|
||||
| 输出 | committed/accepted、operation_id、结果引用、版本 |
|
||||
| 错误 | 稳定 code、可重试分类、冲突、结果未知、权限隐藏规则 |
|
||||
| 预算 | deadline、排队时间、最大重试、载荷、并发/速率 |
|
||||
| 事件 | 成功后产生哪些事实、订阅语义、顺序与重放 |
|
||||
| 演进与验收 | 支持版本、破坏性检测、测试、弃用日期、监控 |
|
||||
|
||||
HTTP 可用 OpenAPI 描述请求响应,异步通道可用 AsyncAPI 描述消息与操作;它们不足以独自表达全部事务、幂等和授权规则,须附语义契约。[OpenAPI 3.1.1](https://spec.openapis.org/oas/v3.1.1.html)、[AsyncAPI 3.0.0](https://www.asyncapi.com/docs/reference/specification/v3.0.0)
|
||||
|
||||
### 4.2 通用元数据
|
||||
|
||||
请求元数据包含 request_id、traceparent、operation、contract_version、remaining_budget、idempotency_key(适用时)、expected_version(适用时)。认证身份与租户由可信入口验证建立,不因请求体有 user_id 就信任它。重试沿用业务幂等键,每次传输 attempt_id 不同。
|
||||
|
||||
响应明确区分:
|
||||
|
||||
- committed:契约规定的持久状态已提交。
|
||||
- accepted:工作已可靠入队,返回可授权查询的 operation_id。
|
||||
- rejected:已确定未执行的业务或权限拒绝。
|
||||
- unknown:超时/断连后无法判断提交是否发生,必须查询或同键重试。
|
||||
- partial:只允许在契约明确哪些子结果成功、如何恢复时使用。
|
||||
|
||||
错误至少包含稳定 code、安全 message、request_id,可选 operation_id、retry_after 和公开 details。禁止返回 SQL、堆栈、内部路径或秘密。HTTP/RPC 状态映射集中维护,业务不得靠文本匹配。
|
||||
|
||||
### 4.3 事件信封
|
||||
|
||||
MCS-09:事件 MUST 有唯一事件身份、来源、事实名称、payload schema、发生时间、租户/环境、聚合标识、correlation/causation、内容摘要。认证上下文只携带最小审计引用,不在消息里长期存放用户 bearer token。
|
||||
|
||||
采用 CloudEvents 时,specversion 指信封标准版本;业务 schema 使用 dataschema 或明确的扩展字段,两者不能混为一谈。使用 (source, id) 标识不同事实;同一事实的投递重试不生成新事件身份。[CloudEvents 1.0.2](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md)
|
||||
|
||||
payload 大小上限、字段可见性、保留期写入契约。大内容用不可变对象引用与摘要,引用必须在消费和重放窗口内可取;写入对象再提交 DB 指针时要管理孤儿对象,不能声称跨对象存储与数据库原子提交。
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据完整性与并发
|
||||
|
||||
MCS-10:每张业务表、对象命名空间和状态源 MUST 只有一个逻辑写入 owner。owner 的多个副本可以并行写,唯一约束/版本校验保证一致性。其他模块通过命令修改,不能“方便起见”直连写表。跨模块读取优先查询契约或订阅生成的只读投影,临时跨表读取须登记耦合与退出条件。
|
||||
|
||||
共用数据库实例 MAY,共用超级用户写权限 SHOULD NOT。L1 至少用静态所有权检查;L2/L3 SHOULD 加数据库 role/schema ACL。迁移由 owner 维护,跨模块外键、视图和级联删除必须显式登记。
|
||||
|
||||
### 5.1 事务边界
|
||||
|
||||
MCS-11:业务状态与对应 outbox MUST 在同一原子事务中提交,或使用能证明同等保证的事务日志/CDC 方案。先写 DB 再直接发消息会留下双写失败窗口。[事务 outbox 模式](https://docs.aws.amazon.com/en_en/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html)
|
||||
|
||||
同数据库、同应用边界可由用例管理明确的 Unit of Work,各数据 owner 执行自己的写操作。业务调用方不接触底层事务对象。跨模块共享事务是有意的一致性耦合,必须记录;不能承诺以后可零成本换成 RPC。
|
||||
|
||||
跨进程、跨库不能传播本地 Tx 伪造原子性。跨域流程采用持久编排/Saga、预留资源、超时释放与补偿;补偿是新的业务动作,不等于撤销现实世界已发生的一切。必须定义中间态、对用户的可见性和需要人工处理的终态。
|
||||
|
||||
### 5.2 约束与锁选型
|
||||
|
||||
| 问题 | 首选机制 | 注意事项 |
|
||||
|---|---|---|
|
||||
| 重复创建/同业务号 | 唯一约束 + 幂等收据 | 单纯先查后写存在竞争 |
|
||||
| 同一聚合并发变更 | version/CAS + 条件更新 | 0 行更新是冲突,不能静默覆盖 |
|
||||
| 必须串行维护多行不变量 | 短事务 + 行锁/适当隔离级别 | 固定锁顺序、死锁/序列化失败有界重试 |
|
||||
| 队列任务竞争领取 | 原子 claim + lease | 领取成功不等于副作用完成 |
|
||||
| 长任务排他 | 租约 + token/epoch + 资源端拒旧写 | TTL 锁本身无法阻止暂停后复活的旧 worker |
|
||||
| 进程内缓存 | 互斥/并发容器 | 不能保证多进程一致性 |
|
||||
|
||||
MCS-12:任何 DB 锁/事务不得跨越 LLM、HTTP、长文件 IO 或等待用户。计算在事务外进行,提交时重新验证权限、状态和 expected_version。
|
||||
|
||||
SKIP LOCKED 适合竞争领取队列,返回的是不完整视图,不能把它当普通一致性查询或完整串行化证明。[PostgreSQL SELECT](https://www.postgresql.org/docs/current/sql-select.html)
|
||||
|
||||
### 5.3 端到端幂等
|
||||
|
||||
MCS-13:幂等必须由实际副作用 owner 保证;总线去重不能代替业务幂等。
|
||||
|
||||
- 幂等作用域为 tenant + operation + caller namespace + key;记录规范化请求摘要、执行状态、结果/结果引用和期限。
|
||||
- 同键同摘要:返回已有结果,或返回正在执行的 operation;不得并行再执行。
|
||||
- 同键不同摘要:返回明确冲突,不能覆写收据或当新请求。
|
||||
- 本地 DB 效果与成功收据同事务;ack 在事务提交之后。
|
||||
- 外部 API/发信/扣费:使用对方幂等能力或业务唯一号,并有查询对账;无法可靠去重的副作用须声明风险与人工恢复路径。
|
||||
- 保留期必须覆盖客户端重试、队列积压、故障恢复与可重放窗口;过期后拒绝旧键、保留业务永久唯一约束或采用明确的新请求规则。
|
||||
|
||||
进程执行后崩溃、响应丢失、消息重复到达均须测试。“至少一次投递 + 幂等可见效果”是默认承诺;不能概括为跨所有外部系统 exactly once。幂等键复用与迟到重试的语义必须明确。[AWS 幂等 API 设计](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/)
|
||||
|
||||
---
|
||||
|
||||
## 6. 可靠总线与持久调度
|
||||
|
||||
### 6.1 交付状态与恢复
|
||||
|
||||
MCS-14:每个订阅者维护独立 delivery,使用有界重试、死信/隔离和可审计重放。状态至少表达 pending、leased、retry_wait、succeeded、dead/quarantined。
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> pending
|
||||
pending --> leased: 原子领取
|
||||
retry_wait --> leased: 到期领取
|
||||
leased --> succeeded: 效果及收据提交后确认
|
||||
leased --> retry_wait: 瞬时错误或租约过期
|
||||
leased --> quarantined: 非法来源或不兼容契约
|
||||
leased --> dead: 永久业务失败或重试耗尽
|
||||
dead --> pending: 审批后重放并保留事件身份
|
||||
quarantined --> pending: 修正条件后受控重放
|
||||
```
|
||||
|
||||
只领取执行容量内的消息;若预取多条,等待中的租约也必须续期或计入排队预算。租约续期失败后停止新副作用;ack/续租/结果写入须检查当前 owner + token。随机 token 可以做“当前持有者相等检查”,只有资源端能比较并拒绝旧代次的单调 epoch 才可宣称有序 fencing。
|
||||
|
||||
处理失败分类:网络波动等可重试;权限/租户/来源/schema 不符隔离;确定的业务拒绝终结;依赖关闭暂停或延后;未知外部结果先查询对账。重放必须重新鉴权,保留原事件身份与新的 replay_id,不能清掉收据来强迫重复副作用。
|
||||
|
||||
### 6.2 顺序与并行
|
||||
|
||||
MCS-15:顺序范围 MUST 写明 tenant/environment + consumer + aggregate,默认只要求聚合内所需顺序,聚合间并行,不强制全局串行。
|
||||
|
||||
领域版本、事件序号和全局表 ID 各有用途,不能互换。订阅者收到的事件可能只是领域事件子集,序号有空洞不必然代表丢消息。
|
||||
|
||||
| 业务需要 | 契约与策略 |
|
||||
|---|---|
|
||||
| 可交换/幂等副作用 | 不强制排序,靠收据去重 |
|
||||
| 用最新完整快照更新投影 | 比较源版本,允许首次高版本 bootstrap,拒绝回退 |
|
||||
| 必须按顺序应用增量 | 独立、无缺口且订阅语义一致的 sequence;定义首序号与恢复 |
|
||||
| 缺号、毒消息 | 只阻塞相应聚合/订阅者,告警并从权威源补齐或重建 |
|
||||
|
||||
不得直接用 aggregate_version == checkpoint + 1 判断所有事件可执行。选择连续 sequence 时须处理过滤事件、事务回滚、补偿、新订阅、迁移与归档,不能以数据库 sequence 自增就假定无缺口。
|
||||
|
||||
### 6.3 调度器
|
||||
|
||||
MCS-16:长流程的任务接受、状态和恢复 MUST 持久化;定时器到期等价于有幂等身份的命令投递,不能只靠进程内 timer。
|
||||
|
||||
业务流程状态至少包含 operation_id、输入快照、当前步骤、各步骤幂等键、attempt、lease/epoch、deadline、结果引用、补偿进度、最后错误。依赖 DAG 与业务循环分开表达。优先级、租户公平性、配额、排队上限和取消行为必须声明。
|
||||
|
||||
取消是请求,不保证已提交副作用消失。结果未知先对账;补偿和人工介入有独立权限及审计。流程定义升级后,运行中实例继续绑定原版本,除非存在明确迁移规则。
|
||||
|
||||
### 6.4 端到端交付顺序
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as 调用方
|
||||
participant A as 模块A
|
||||
participant D as A数据库
|
||||
participant B as 事件总线
|
||||
participant W as 模块B消费者
|
||||
participant E as B数据库
|
||||
C->>A: 命令 + 幂等键 + expected_version
|
||||
A->>D: 同事务写业务、收据、outbox
|
||||
D-->>A: commit
|
||||
A-->>C: committed 或 operation_id
|
||||
D->>B: relay 发送,允许重复
|
||||
B->>W: 独立订阅投递
|
||||
W->>E: 同事务校验收据并写效果
|
||||
E-->>W: commit
|
||||
W-->>B: ack
|
||||
```
|
||||
|
||||
上图只保证各自数据库内原子性;relay 到 broker、消费者到外部服务的窗口分别依靠去重、查询和恢复契约处理。
|
||||
|
||||
---
|
||||
|
||||
## 7. 故障隔离、安全与可观测性
|
||||
|
||||
MCS-17:每个消费者/依赖/租户 MUST 有有界执行预算,至少包括并发、队列长度、载荷、处理时限与重试总预算。独立队列、worker pool、连接池配额用于防止一个慢模块耗尽全部资源。仅捕获异常不构成隔离。
|
||||
|
||||
重试由一层统一协调,避免客户端、服务、broker 各重试三次形成放大。只对契约允许的错误重试,采用退避和抖动;非幂等写禁止盲目重试或并发 hedging。gRPC 的传输重试策略也需纳入总预算。[gRPC retry](https://grpc.io/docs/guides/retry/)
|
||||
|
||||
MCS-18:跨信任边界 MUST 验证服务身份、调用权限、租户和资源权限;同机 IPC 也不能默认可信。用户委托与服务自身权限分开;消费者按契约确定“重验当前权限”或“执行已批准且未撤销的操作凭证”,不得直接相信事件里的 actor_id。总线 publish/subscribe、死信读取、重放和流程补偿分别授权。
|
||||
|
||||
MCS-19:日志与 trace MUST 能关联 request、operation、event、delivery、aggregate 和 attempt;指标至少含错误/延迟、outbox 最老年龄、消费者 lag、重试/死信、幂等冲突、lease 丢失、隔离队列深度。高基数租户/事件 ID 放日志或 trace,避免无界指标标签。
|
||||
|
||||
MCS-20:readiness 只报告该进程能否履行职责,liveness 检查是否存活。非核心下游故障不应触发全系统重启风暴。停机先停止接受/领取,排空到预算,之后安全交还租约;崩溃恢复通过持久状态和收据继续。
|
||||
|
||||
### 7.1 容量与故障目标
|
||||
|
||||
每个系统必须填写负载基线、p95/p99、错误预算、排队/恢复时限、RPO/RTO 和故障注入标准,不能照抄统一常数。采用真实容量测试验证:入站速率、平均处理时间和目标利用率决定并发预算;最老消息年龄和排队尾延迟比单纯队列长度更有意义。
|
||||
|
||||
必须识别总线、数据库和公共认证作为共享故障点。是否集群、高可用和跨区由 SLO 决定;总线高可用不能替代数据库备份恢复。数据库提交的耐久保证以实际复制与故障转移配置为准。
|
||||
|
||||
---
|
||||
|
||||
## 8. 演进、测试和可执行门禁
|
||||
|
||||
MCS-21:契约 MUST 有兼容矩阵和弃用窗口;新增可选字段须验证旧消费者未知字段策略,枚举新增也可能破坏客户端。删除字段、改变含义/单位/权限/错误语义属于破坏性变更。采用 expand → 双版本兼容 → 消费者迁移 → contract,回滚窗口内保留兼容能力。
|
||||
|
||||
每个模块的公开契约、数据库迁移、事件 schema、SDK 版本分别跟踪。新旧版本混跑必须验证,不通过一次“全系统同时升级”掩盖运行耦合。
|
||||
|
||||
### 8.1 合并与发布证据
|
||||
|
||||
边界、契约和权限是共同基线;数据库、可靠事件、外部副作用、租约、多租户及跨进程测试按系统实际声明的能力触发。设计卡/manifest MUST 记录适用能力和不适用理由;不能因为某项测试需要设施而给本不需要该设施的模块强加架构。
|
||||
|
||||
门禁区分 PASS、FAIL、SKIP(适用但未执行)与 N/A(经核对不适用)。N/A 不等于 SKIP,不需要作为到期违规例外;若业务需要该能力但尚未实现,则不能标 N/A。下表和 §8.2 均遵循此规则,参考平台仍须覆盖其声明完整组合中的全部适用场景。
|
||||
|
||||
| 门禁 | 必需证据 | 不能替代它的证据 |
|
||||
|---|---|---|
|
||||
| 边界 | 依赖图、无环、无内部包越界、数据所有权 | 目录拆分、文件行数 |
|
||||
| 契约 | schema lint、破坏性 diff、提供者/消费者兼容测试 | 仅生成代码成功 |
|
||||
| 正确性 | 公开用例不变量、权限负例、CAS/唯一约束 | handler mock |
|
||||
| 一致性 | 真数据库并发、回滚、收据和 outbox 同事务 | 单进程 mutex 单测 |
|
||||
| 交付 | 真传输的丢 ack、重复、乱序、断网、重启恢复 | 内存 bus |
|
||||
| 隔离 | 慢/崩溃消费者下其他模块仍达到预算 | 一个 handler 返回 error |
|
||||
| 运维 | 新旧版本混跑、drain、DLQ 重放、备份恢复 | health 为 200 |
|
||||
|
||||
MCS-22:例外 MUST 有 owner、原因、范围、到期与移除计划。门禁状态明确为 proposed/manual/enforced,只有工具接入及正负例验证后才能写 enforced。初始遗留问题可按精确基线逐项收敛,不得无限扩大 allowlist 或移动基线掩盖新增耦合。
|
||||
|
||||
### 8.2 最小验收场景集
|
||||
|
||||
1. 同一幂等键 100 个并发请求,只有一个业务效果;同键异载荷明确冲突。
|
||||
2. 两个不同键竞争同一 expected_version,只有一个成功,其余返回冲突。
|
||||
3. 业务写失败时 outbox 不产生;提交后 relay 退出,恢复后消息仍能交付。
|
||||
4. 消费者效果提交后 ack 丢失,重投不重复效果。
|
||||
5. 外部调用成功但本地未知,查询/同键恢复后结果一致。
|
||||
6. worker 租约过期后旧执行者恢复,资源端拒绝旧写。
|
||||
7. 首条事件为高领域版本、过滤造成版本空洞、乱序与重复,按所选顺序契约处理。
|
||||
8. 一个消费者持续超时/毒消息,其余消费者和聚合仍满足各自预算。
|
||||
9. 跨租户命令、伪造事件来源、越权订阅/重放均拒绝。
|
||||
10. 队列饱和返回过载或明确 accepted;不得已经丢弃却返回成功。
|
||||
11. 新旧契约版本混跑、未知枚举、退出排空与断线重连符合声明。
|
||||
12. 备份恢复后收据、outbox、投影/业务状态保持可对账,不自动重新执行不可逆副作用。
|
||||
|
||||
---
|
||||
|
||||
## 9. 采用方法
|
||||
|
||||
新项目先完成模块设计卡、契约样例、数据所有权图和关键故障目标,再写业务实现。优先交付一个真实端到端能力:同步命令 → 原子提交 → 可靠事件 → 幂等消费者 → 重启恢复。
|
||||
|
||||
第一版平台只实现一个主语言 SDK、进程内适配器、一种跨进程传输和一种持久事件实现。需要多语言时,复用 IDL 与一致性测试向量,各语言实现相同语义,而非共享同一份业务代码。
|
||||
|
||||
每次新增模块都回答三个问题:调用者是否需要理解内部实现;出故障能否只定位/替换该模块;验证契约时是否必须启动全部系统。答案若长期不理想,应调整职责和接口,而不是继续增加通信中间层。
|
||||
Reference in New Issue
Block a user