怎么用这份清单
这份清单不是评分表,也不是审批门槛。它的用途是:在一次架构评审前,快速判断“哪些问题有答案、哪些问题还没被问到”。
使用方法:
- 评审前,主持人按九个部分逐项过一遍,标出“有材料”“口头说过”“完全没提”;
- 评审时,只讨论“口头说过”和“完全没提”的项,避免在已经有材料的地方重复耗时;
- 评审后,把未通过项转成行动项,写清负责人和关闭条件;
- 下次评审,先检查上次行动项的关闭情况。
每项有三个状态:
| 状态 | 含义 | 处理 |
|---|---|---|
| 有材料 | 有文档、代码、测试或数据支撑 | 快速确认,不展开 |
| 口头说过 | 会上有人说,但没有落文档或验证 | 本次评审必须落成行动项 |
| 没提 | 完全没涉及 | 判断是否适用;适用则补,不适用则记录理由 |
清单不要求全部通过。它要求每个“不通过”都有明确的理由和后续动作。
一、问题与约束
| 检查项 | 通过标准 | 红旗 |
|---|---|---|
| 要解决的问题 | 一句话说清现状痛点与目标 | 通篇讲方案,没说为什么现在做 |
| 业务目标 | 有可衡量的业务指标 | 只有“提升体验”“降本增效” |
| 技术约束 | 列出团队、预算、合规、遗留系统限制 | 假设可以随意重构、随意招人 |
| 非功能目标 | 延迟、可用性、容量有数字或区间 | 只写“高性能”“高可用” |
| 不做什么 | 明确本期范围外的事项 | 范围无限扩张,评审无法收敛 |
| 成功标准 | 上线后用什么指标判断成功 | 没有回滚或失败判据 |
| 关键干系人 | 谁决策、谁执行、谁验收 | 评审现场才发现缺关键角色 |
| 时间与阶段 | 有里程碑和依赖关系 | 所有事情都“尽快” |
二、业务边界
| 检查项 | 通过标准 | 红旗 |
|---|---|---|
| 子域划分 | 核心、支撑、通用域有依据 | 按技术分层划域 |
| 限界上下文 | 每个上下文有职责、数据和 owner | 一个上下文包揽全部业务 |
| 上下文映射 | 上下游关系与集成模式明确 | 所有上下文共享一个库 |
| 统一语言 | 关键术语有定义且代码一致 | 同一概念三个名字 |
| 聚合边界 | 聚合内不变式清晰 | 一个聚合覆盖整个订单流程 |
| 跨聚合一致性 | 明确同步或最终一致 | 假设所有操作都能一个事务 |
| 外部系统 | 哪些是外部、哪些自建有判断 | 把库存当外部服务却共享数据库 |
| 团队归属 | 每个边界有负责团队 | 边界画了但没人认领 |
三、模块与依赖
| 检查项 | 通过标准 | 红旗 |
|---|---|---|
| 模块清单 | 每个模块有职责与公开接口 | 模块按 controller/service/dao 划分 |
| 依赖方向 | 外层依赖内层,有检查命令 | 只有口头约定 |
| 循环依赖 | 不存在,或有明确例外 | 用 init 或反射绕过 |
| 数据所有权 | 每张表只有一个写 owner | 多模块直接写同一张表 |
| 端口设计 | 领域依赖抽象,不依赖实现 | 领域包 import 数据库驱动 |
| 适配器 | 外部实现可替换、可测试 | 测试必须启动真实数据库 |
| 共享代码 | 共享内核有 owner 和版本 | 一个 utils 包被所有模块依赖 |
| 拆分预案 | 哪个模块先拆、怎么拆有步骤 | 只写“以后再说” |
四、接口与契约
| 检查项 | 通过标准 | 红旗 |
|---|---|---|
| 资源边界 | 资源与业务概念对应 | RPC 式动词堆在 URL 里 |
| 命令语义 | 创建、取消、支付等命令有幂等约定 | 所有接口都假设只调用一次 |
| 契约来源 | OpenAPI 或等价契约是唯一事实来源 | 文档与代码各写一份 |
| 错误模型 | 错误码稳定、可分支 | 调用方匹配错误文本 |
| 版本策略 | 兼容窗口与弃用流程明确 | 直接改字段含义 |
| 分页与批量 | 有上限、有游标或稳定排序 | 无上限全量返回 |
| 超时与重试 | 每个接口有超时预算 | 客户端无限重试 |
| 契约测试 | 消费者契约有自动化验证 | 上线后靠联调发现问题 |
五、数据与一致性
| 检查项 | 通过标准 | 红旗 |
|---|---|---|
| 存储选型 | 有 ADR,写清备选与否决理由 | 默认选最熟悉的数据库 |
| 事务边界 | 哪些操作必须本地事务明确 | 一个请求跨三个库还称强一致 |
| 出站表 | 状态与事件同事务写入 | 先提交再发消息 |
| 幂等策略 | 键、窗口、冲突处理明确 | 只靠数据库唯一索引 |
| 读模型 | 是否 CQRS 有引入判据 | 为了 CQRS 而 CQRS |
| 对账 | 定期对账与差异修复流程 | 假设消息不会丢 |
| 数据迁移 | 向前兼容、可回滚或可双写 | 大表直接改字段 |
| 备份恢复 | RPO/RTO 有数字并演练 | 只写“有备份” |
六、异步与幂等
| 检查项 | 通过标准 | 红旗 |
|---|---|---|
| 同步/异步判断 | 每个链路有选择理由 | 所有调用都改成异步 |
| 事件语义 | 事件、命令、消息区分清楚 | 用事件表达“请做某事” |
| 投递语义 | 至少一次、至多一次或恰好一次明确 | 声称“保证不重复”却无去重 |
| 消费幂等 | 消费者按事件 ID 去重 | 重复消费导致重复扣款 |
| 顺序 | 是否需要顺序、如何保证明确 | 假设消息天然有序 |
| 积压 | 有监控、限流和降级 | 积压后只能重启 |
| 死信 | 有死信队列和处理流程 | 失败消息直接丢弃 |
| 补偿 | 失败路径有补偿或人工兜底 | 只设计成功路径 |
七、安全与信任
| 检查项 | 通过标准 | 红旗 |
|---|---|---|
| 身份认证 | 用户与服务身份分开 | 用用户 token 调内部服务 |
| 授权 | 每个资源操作校验归属 | 只在前端隐藏按钮 |
| 输入边界 | 校验、限长、拒绝未知字段 | 直接透传外部 JSON |
| 数据分级 | PII、支付、日志有分类 | 所有数据同等对待 |
| 密钥管理 | 密钥不入库、不入镜像、可轮换 | 配置里写明文密码 |
| 第三方回调 | 验签、防重放、金额核对 | 只信回调里的状态字段 |
| 审计 | 关键操作有不可篡改记录 | 只写应用日志 |
| 越权测试 | 有自动化用例 | 靠人工点页面 |
八、部署与运维
| 检查项 | 通过标准 | 红旗 |
|---|---|---|
| 部署单元 | 与模块边界、故障域一致 | 一个进程塞所有东西还叫微服务 |
| 故障域 | 单点已识别并有冗余方案 | 数据库单点无切换演练 |
| 配置管理 | 环境差异显式、可审计 | 手工改生产配置 |
| 健康检查 | 区分存活与就绪 | 进程活着就接流量 |
| 发布策略 | 滚动、蓝绿或金丝雀有选择理由 | 直接全量替换 |
| 回滚 | 版本与数据都能回滚 | 只回滚代码不回滚 schema |
| 容量 | 有基线、有余量、有扩容触发条件 | 上线前没压测 |
| 观测 | 日志、指标、追踪能回答“卡在哪” | 只有访问日志 |
九、演进与治理
| 检查项 | 通过标准 | 红旗 |
|---|---|---|
| 迁移策略 | 绞杀者、双写或分支抽象有步骤 | 一次性重写 |
| 兼容窗口 | 新旧版本共存期明确 | 强制所有调用方同时升级 |
| 开关 | 有 owner、默认值、到期日 | 临时开关永久存在 |
| 适应度函数 | 关键约束有自动化检查 | 架构规则只写在 Wiki |
| 决策记录 | 重要决策有 ADR | 决策只在会议纪要里 |
| 例外流程 | 偏离标准有审批与复审 | 例外变成常态 |
| 技术债 | 有台账、有偿还计划 | 只记录不排序 |
| 复盘 | 故障有改进项和关闭验证 | 复盘只写“加强监控” |
十条常见失败模式
第一,方案很大,问题很小。评审花了三小时讲平台化,最后发现当前痛点是订单状态机没有约束。先确认问题,再谈方案。
第二,所有质量属性都是形容词。“高可用、低延迟、可扩展”没有数字,无法验收,也无法比较方案。
第三,边界画在组织图上。按现有团队分服务,而不是按业务能力和数据所有权划分,结果团队一变,系统就要重拆。
第四,把共享数据库当集成方式。两个服务读写同一张表,却声称“独立部署”;任何 schema 变更都会同时影响两边。
第五,用消息队列掩盖一致性问题。发消息不等于最终一致;没有对账、没有补偿、没有收敛期限,只是把不一致藏得更深。
第六,接口没有错误契约。调用方按错误文本分支,翻译一次就全坏;错误码不稳定,重试策略也无从设计。
第七,安全只在网关做。内部服务互相信任,一个被攻破的实例可以横向访问所有数据。
第八,发布没有回滚。代码能回滚,数据迁移不能回滚;上线前没想清楚兼容窗口。
第九,架构规则没有检查。依赖方向、模块边界、命名约定都写在文档里,CI 里一条都不跑。
第十,评审没有结论。会上每个人都说了意见,散会后没有行动项、没有负责人、没有关闭条件;三个月后同样的问题再开一次会。
评审记录模板
评审记录不需要很长,但必须能回答“谁、什么时候、基于什么材料、得出了什么结论”。
评审主题:
日期与参与角色:
输入材料:
- 架构视图(版本/链接):
- ADR 列表:
- 风险台账:
- 压测或实验数据:
一、已确认的约束
- 业务:
- 技术:
- 组织:
二、逐项结论
| 议题 | 结论(接受/附条件/否决/待验证) | 理由 | 负责人 | 关闭条件 |
三、未采纳的反对意见
- 意见:
- 否决理由:
- 重新评估触发条件:
四、行动项
| 行动 | 负责人 | 截止时间 | 验收方式 |
五、遗留风险
| 风险 | 等级 | 负责人 | 缓解措施 | 关闭条件 |
模板里最重要的是“未采纳的反对意见”和“关闭条件”。前者保留未来重新评估的线索,后者防止行动项永远停在“进行中”。
结论口径
评审结论建议只用四种,避免“基本同意”“原则上通过”这类无法执行的表述:
| 结论 | 含义 | 后续动作 |
|---|---|---|
| 接受 | 当前材料足以支撑决策 | 进入实施,按 ADR 记录 |
| 附条件接受 | 方向可行,但有关键条件未满足 | 条件关闭前不得全量上线 |
| 待验证 | 缺少数据或实验,无法判断 | 指定实验、负责人和期限 |
| 否决 | 方案与约束冲突,或代价不可接受 | 记录理由,重新设计 |
“附条件接受”必须写清条件是否可验证。比如“完成崩溃注入,证明提交后事件不丢”是可验证的;“再完善一下监控”不是。
按变更类型裁剪
九个部分不必每次全查。按变更类型裁剪,能让评审时间花在真正的风险上。
| 变更类型 | 必查部分 | 可以简化 |
|---|---|---|
| 新功能上线 | 业务边界、接口契约、数据一致性、安全 | 部署拓扑通常不变 |
| 新服务拆分 | 业务边界、模块依赖、部署运维、演进治理 | 单次接口细节可后置 |
| 存储或 schema 变更 | 数据一致性、部署运维、演进治理 | 业务边界通常不变 |
| 引入消息队列 | 异步幂等、数据一致性、部署运维 | 接口契约变化有限 |
| 对外开放接口 | 接口契约、安全信任、部署运维 | 内部模块依赖可简化 |
| 性能改造 | 数据一致性、部署运维、异步幂等 | 业务边界通常不变 |
| 安全加固 | 安全信任、接口契约、部署运维 | 演进治理可简化 |
| 组织调整 | 业务边界、模块依赖、演进治理 | 具体接口实现可后置 |
裁剪的原则是:变更直接触碰的部分必须查,间接影响的部分至少确认一次“是否受影响”。不能因为“这次只是小改动”就跳过安全或数据一致性——小改动也可能改错一个授权判断。
评审中的提问方式
清单是给主持人用的,不是给汇报人念的。提问方式决定了评审是走过场还是真的发现问题。
- 问“如果这个调用超时,用户看到什么”,而不是“你们考虑超时了吗”;
- 问“这条数据除了这个模块,还有谁会写”,而不是“数据所有权清楚吗”;
- 问“重复消息进来会发生什么”,而不是“幂等做了吗”;
- 问“上次类似改动出了什么问题”,而不是“风险大不大”;
- 问“这个决定什么时候需要重新评估”,而不是“以后怎么办”;
- 问“哪一条证据能证明它”,而不是“有把握吗”。
具体场景比抽象问题更容易得到真实答案。汇报人说“考虑过”时,追一句“那当时选了什么、否决了什么”,通常能立刻分辨是认真设计过,还是会上临时想的。
评审前的十分钟自检
如果时间只够做一件事,用下面八个问题快速扫一遍:
- 这次要解决的业务问题是什么?有没有数字?
- 最贵的三个变更是什么?当前架构让它们更贵还是更便宜?
- 哪些状态迁移是合法的?规则写在哪?
- 哪些数据只能有一个写 owner?现在有几个?
- 一个请求穿过哪些模块?超时预算怎么分?
- 重复请求、重复消息、重复回调,分别由谁去重?
- 出故障时,第一眼从哪里看出卡在哪一步?
- 如果明天要拆掉一个模块,第一步做什么?
八个问题里超过三个答不上来,说明方案还没到可以评审的程度。先把答案补上,再开会。
这份清单不负责什么
清单不判断方案是否优雅,也不替代架构师的经验。它能发现“没被问到的问题”,但发现不了“问到了却答错”的问题;答错只能靠实验、复盘和时间暴露。
清单也不保证所有条目都适用。小团队可以用它做自查,大组织可以按变更类型裁剪;两种用法都不需要把每一行都填满。
真正决定评审质量的,是参会者愿不愿意在“还没有答案”的地方停下来,而不是清单本身有多长。
清单能做的,只是把“停下来”的位置标出来。
小结
这份清单覆盖九个部分:问题与约束、业务边界、模块依赖、接口契约、数据一致性、异步幂等、安全信任、部署运维、演进治理。每项给出通过标准和红旗信号,目的是让评审聚焦在“没有答案的问题”上,而不是重复已有材料。
清单不能替代判断,也不能保证架构正确。它只能降低“重要问题没人问”的概率。真正的结论仍然要落在 ADR、风险台账和行动项上,并在下一次评审时检查关闭情况。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。