架构决策记录
架构决策记录 (Architecture Decision Records)
创建、维护和管理架构决策记录 (ADR) 的全面模式,用于捕捉重大技术决策背后的上下文和原由。
适用场景
- 做出重大架构决策时
- 记录技术选型时
- 记录设计权衡 (Trade-offs) 时
- 新团队成员入职引导时
- 回溯历史决策时
- 建立决策流程时
不适用场景
- 仅需记录细小的实现细节时
- 变更仅为微小补丁或常规维护时
- 没有需要捕捉的架构决策时
操作指南
1. 捕捉决策上下文、约束条件和驱动因素。
2. 记录考虑过的方案及其权衡。
3. 记录最终决策、原由及其后果。
4. 关联相关的 ADR,并随时间更新状态。
核心概念
1. 什么是 ADR?
架构决策记录 (ADR) 包含:
- 上下文 (Context):为什么需要做出这个决策
- 决策 (Decision):我们决定了什么
- 后果 (Consequences):结果会产生什么影响
2. 何时编写 ADR
| 编写 ADR | 跳过 ADR |
|-----------|----------|
| 采用新框架 | 次要版本升级 |
| 数据库技术选型 | Bug 修复 |
| API 设计模式 | 实现细节 |
| 安全架构 | 常规维护 |
| 集成模式 | 配置变更 |
3. ADR 生命周期
Proposed (提议) → Accepted (通过) → Deprecated (弃用) → Superseded (被取代)
↓
Rejected (拒绝)模板
模板 1:标准 ADR (MADR 格式)
# ADR-0001: 使用 PostgreSQL 作为主数据库
状态
Accepted (通过)
上下文
我们需要为新的电子商务平台选择一个主数据库。系统将处理:
- 约 10,000 个并发用户
- 具有层级分类的复杂产品目录
- 订单和支付的事务处理
- 产品的全文搜索
- 门店定位的地理空间查询
团队拥有 MySQL、PostgreSQL 和 MongoDB 的经验。财务交易需要满足 ACID 特性。
决策驱动因素
- 必须满足 ACID 特性 以处理支付
- 必须支持复杂查询 用于报表
- 应支持全文搜索 以降低基础设施复杂度
- 应具有良好的 JSON 支持 以应对灵活的产品属性
- 团队熟悉度 可缩短上手时间
考虑的方案
方案 1: PostgreSQL
- 优点: 符合 ACID 特性,优秀的 JSON 支持 (JSONB),内置全文搜索,PostGIS 支持地理空间,团队有经验
- 缺点: 复制配置比 MySQL 略复杂
方案 2: MySQL
- 优点: 团队非常熟悉,复制简单,社区庞大
- 缺点: JSON 支持较弱,无内置全文搜索(需依赖 Elasticsearch),无原生地理空间支持(需扩展)
方案 3: MongoDB
- 优点: 模式灵活,原生 JSON,水平扩展
- 缺点: (决策时) 多文档事务缺乏 ACID 支持,团队经验有限,需要严格的模式设计纪律
决策
我们将使用 PostgreSQL 15 作为主数据库。
原由
PostgreSQL 在以下方面达到了最佳平衡:
1. ACID 兼容性...
1. ACID 合规性:对于电子商务交易至关重要
2. 内置功能(全文检索、JSONB、PostGIS):降低了基础设施的复杂度
3. 团队熟悉度:对 SQL 数据库的熟悉程度降低了学习曲线
4. 成熟的生态系统:拥有优秀的工具链和社区支持
虽然复制(replication)略显复杂,但相比于减少额外服务(无需单独部署 Elasticsearch)而言,这种复杂度是可以接受的。
后果
正面影响
- 单一数据库即可处理交易、搜索和地理空间查询
- 降低运维复杂度(需要管理的服务更少)
- 为财务数据提供强一致性保证
- 团队可以利用现有的 SQL 专业知识
负面影响
- 需要学习 PostgreSQL 的特定功能(JSONB、全文检索语法)
- 垂直扩展的限制可能导致需要更早地部署只读副本
- 部分团队成员需要进行 PostgreSQL 专项培训
风险
- 全文检索的扩展性可能不如专业的搜索引擎
- 缓解方案:在设计时预留未来必要时添加 Elasticsearch 的可能性
实现注意事项
- 使用 JSONB 处理灵活的产品属性
- 使用 PgBouncer 实现连接池
- 为只读副本配置流复制(streaming replication)
- 使用
pg_trgm扩展实现模糊搜索
相关决策
- ADR-0002:缓存策略 (Redis) —— 与数据库选择互补
- ADR-0005:搜索架构 —— 如果需要 Elasticsearch,可能会取代本决策
参考资料
- 内部文档:
/docs/benchmarks/database-comparison.md中的性能基准测试
### 模板 2:轻量级 ADRADR-0012: 前端开发采用 TypeScript
状态: 已通过
日期: 2024-01-15
决策者: @alice, @bob, @charlie
背景
我们的 React 代码库已增长至 50 多个组件,与 prop 类型不匹配和 undefined 相关的 Bug 报告日益增多。而 PropTypes 仅能提供运行时检查。
决策
所有新前端代码采用 TypeScript。现有代码逐步迁移。
后果
优点: 在编译时捕获类型错误,更好的 IDE 支持,代码自文档化。
缺点: 团队有学习曲线,初期开发速度下降,构建复杂度增加。
缓解措施: 组织 TypeScript 培训,通过设置 allowJs: true 允许渐进式采用。
### 模板 3:Y-Statement 格式ADR-0015: API 网关选型
在 构建微服务架构 的背景下,
面对 需要集中式 API 管理、身份验证和限流 的需求,
我们决定选择 Kong Gateway,
而非 AWS API Gateway 和自定义 Nginx 方案,
旨在实现 供应商无关性、插件可扩展性以及团队对 Lua 的熟悉度,
并接受 我们需要自行管理 Kong 基础设施 这一事实。
### 模板 4:弃用 ADRADR-0020: 弃用 MongoDB 改用 PostgreSQL
状态
已通过(取代 ADR-0003)
背景
ADR-0003 (2021) 因模式(schema)灵活性需求而选择 MongoDB 存储用户配置文件。此后:
- MongoDB 的多文档事务在我们的用例中仍然存在问题
- 我们的模式已趋于稳定,极少变更
- 我们在其他服务中积累了 PostgreSQL 的专业经验
- 同时维护两种数据库增加了运维负担
决策
弃用 MongoDB 并将用户配置文件迁移至 PostgreSQL。
迁移计划
1. 第一阶段(我们...
1. 第一阶段(第 1-2 周):创建 PostgreSQL 模式,启用双写
2. 第二阶段(第 3-4 周):回填历史数据,验证一致性
3. 第三阶段(第 5 周):将读取切换至 PostgreSQL,进行监控
4. 第四阶段(第 6 周):移除 MongoDB 写入,停用旧库
影响
正面影响
- 统一数据库技术,降低运维复杂度
- 用户数据支持 ACID 事务
- 团队可集中提升 PostgreSQL 专业能力
负面影响
- 迁移工作量(约 4 周)
- 迁移过程中存在数据问题的风险
- 失去部分模式(Schema)灵活性
经验教训
基于 ADR-0003 的经验总结:
- 过高估计了模式灵活性的收益
- 低估了维护多个数据库的运维成本
- 在技术决策时应考虑长期维护成本
模板 5:征求意见稿 (RFC) 风格
# RFC-0025:在订单管理中采用事件溯源 (Event Sourcing)
摘要
建议在订单管理领域采用事件溯源模式,以提高可审计性,支持时间点查询并赋能业务分析。
动机
当前面临的挑战:
1. 审计需求需要完整的订单历史记录
2. 无法查询“订单在时间点 X 的状态是什么?”
3. 分析团队需要事件流来构建实时仪表盘
4. 客服人员手动重建订单状态,效率低下
详细设计
事件存储 (Event Store)
### 投影 (Projections)
- CurrentOrderState:用于查询的物化视图
- OrderHistory:用于审计的完整时间线
- DailyOrderMetrics:分析聚合数据
技术选型
- 事件存储:EventStoreDB(专用数据库,支持投影)
- 考虑过的替代方案:Kafka + 自定义投影服务
缺点
- 团队学习曲线较陡
- 相比 CRUD 复杂度增加
- 需要谨慎设计事件(存储后不可更改)
- 存储量持续增长(事件永不删除)
替代方案
1. 审计表:更简单,但不支持时间点查询
2. 现有数据库的 CDC:实现复杂,且未改变数据模型
3. 混合模式:仅对订单状态变更使用事件溯源
待解决问题
- [ ] 事件模式的版本管理策略
- [ ] 事件的保留策略
- [ ] 为了性能而设置的快照频率
实施计划
1. 针对单一订单类型进行原型开发(2 周)
2. 开展事件溯源团队培训(1 周)
3. 全面实施与迁移(4 周)
4. 持续监控与优化(长期)
参考资料
ADR 管理
目录结构
docs/
├── adr/
│ ├── README.md # 索引与指南
│ ├── template.md # 团队 ADR 模板
│ ├── 0001-use-postgresql.md
│ ├── 0002-caching-strategy.md
│ ├── 0003-mongodb-user-profiles.md # [已弃用]
│ └── 0020-deprecate-mongodb.md # 取代 0003ADR 索引 (README.md)
# 架构决策记录 (ADR)
本目录包含 [项目名称] 的架构决策记录 (ADR)。
索引
| ADR | 标题 | 状态 | 日期 |
|-----|-------|--------|------|
| 0001 | 使用 PostgreSQL 作为主数据库 | 已接受 | 2024-01-10 |
| 0002 | 使用 Redis 的缓存策略 | 已接受 |
epted | 2024-01-12 |
| 0003 | MongoDB for User Profiles | Deprecated | 2023-06-15 |
| 0020 | Deprecate MongoDB | Accepted | 2024-01-15 |
创建新的 ADR
1. 将 template.md 复制为 NNNN-title-with-dashes.md
2. 填写模板
3. 提交 PR 进行评审
4. 批准后更新此索引
ADR 状态
- Proposed (提议):讨论中
- Accepted (通过):已决策,正在实施
- Deprecated (弃用):不再适用
- Superseded (被取代):被另一个 ADR 取代
- Rejected (拒绝):经过考虑但未采用
### 自动化 (adr-tools)安装 adr-tools
brew install adr-tools初始化 ADR 目录
adr init docs/adr创建新 ADR
adr new "Use PostgreSQL as Primary Database"取代某个 ADR
adr new -s 3 "Deprecate MongoDB in Favor of PostgreSQL"生成目录
adr generate toc > docs/adr/README.md关联相关 ADR
adr link 2 "Complements" 1 "Is complemented by"## 评审流程ADR 评审清单
提交前
- [ ] 上下文清晰地解释了问题
- [ ] 考虑了所有可行方案
- [ ] 优缺点分析客观平衡
- [ ] 记录了后果(正面和负面)
- [ ] 关联了相关的 ADR
评审期间
- [ ] 至少 2 名资深工程师评审
- [ ] 咨询了受影响的团队
- [ ] 考虑了安全性影响
- [ ] 记录了成本影响
- [ ] 评估了可逆性
通过后
- [ ] 更新 ADR 索引
- [ ] 通知团队
- [ ] 创建实施任务单 (Tickets)
- [ ] 更新相关文档
最佳实践
建议 (Do's)
- 尽早编写 ADR —— 在实施开始之前
- 保持简洁 —— 最多 1-2 页
- 诚实面对权衡 —— 包含真实的缺点
- 关联相关决策 —— 构建决策图谱
- 更新状态 —— 被取代时及时标记为弃用
禁忌 (Don'ts)
- 不要修改已通过的 ADR —— 编写新的 ADR 来取代它
- 不要跳过上下文 —— 后续阅读者需要背景信息
- 不要隐藏失败 —— 被拒绝的决策同样有价值
- 不要含糊其辞 —— 具体的决策,具体的后果
- 不要忘记实施 —— 没有行动的 ADR 是浪费
资源
局限性
- 仅在任务明确符合上述范围时使用此技能。
- 不要将输出结果视为环境特定验证、测试或专家评审的替代方案。
- 如果缺少必要的输入、权限、安全边界或成功标准,请停止并请求澄清。