name: 架构决策记录 description: 编写和维护架构决策记录(ADRs),遵循技术决策文档化的最佳实践。适用于记录重要技术决策、回顾过往架构选择或建立决策流程。 license: MIT metadata: version: “1.0.0” domain: 架构 triggers: ADR, 架构决策记录, 技术决策, 决策文档化 role: 架构师 scope: 系统设计 output-format: 架构 related-skills: 架构模式
架构决策记录
创建、维护和管理架构决策记录(ADRs)的综合模式,捕获重要技术决策的背景和理由。
使用此技能的时机
- 做出重要架构决策
- 文档化技术选择
- 记录设计权衡
- 新团队成员入职
- 回顾历史决策
- 建立决策流程
不应使用此技能的时机
- 只需记录小的实现细节
- 更改是小的补丁或日常维护
- 没有架构决策需要捕获
指令
- 捕获决策背景、约束和驱动因素。
- 文档化考虑的选项及其权衡。
- 记录决策、理由和后果。
- 链接相关ADRs并随时间更新状态。
核心概念
1. 什么是ADR?
一个架构决策记录捕获:
- 背景:为什么需要做出决策
- 决策:我们决定了什么
- 后果:结果会发生什么
2. 何时编写ADR
| 编写ADR | 跳过ADR |
|---|---|
| 新框架采用 | 次要版本升级 |
| 数据库技术选择 | 错误修复 |
| API设计模式 | 实现细节 |
| 安全架构 | 日常维护 |
| 集成模式 | 配置更改 |
3. ADR生命周期
提议 → 接受 → 弃用 → 被取代
↓
拒绝
模板
模板1:标准ADR(MADR格式)
# ADR-0001: 使用PostgreSQL作为主数据库
## 状态
已接受
## 背景
我们需要为新电商平台选择主数据库。系统将处理:
- 约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合规性** 对电商交易至关重要
2. **内置能力**(全文搜索、JSONB、PostGIS)减少基础设施复杂性
3. **团队熟悉度** 对SQL数据库减少学习曲线
4. **成熟生态系统** 有优秀的工具和社区支持
复制的小复杂性被减少额外服务(无需单独的Elasticsearch)所抵消。
## 后果
### 积极
- 单一数据库处理交易、搜索和地理空间查询
- 减少运营复杂性(管理服务较少)
- 金融数据的强一致性保证
- 团队可杠杆现有SQL专业知识
### 消极
- 需要学习PostgreSQL特定功能(JSONB、全文搜索语法)
- 垂直扩展限制可能较早需要读副本
- 一些团队成员需要PostgreSQL特定培训
### 风险
- 全文搜索可能不如专用搜索引擎扩展性好
- 缓解:设计为可能需要的Elasticsearch添加
## 实现说明
- 使用JSONB用于灵活的产品属性
- 用PgBouncer实现连接池
- 设置流复制用于读副本
- 使用pg_trgm扩展用于模糊搜索
## 相关决策
- ADR-0002: 缓存策略(Redis)- 补充数据库选择
- ADR-0005: 搜索架构- 如果需Elasticsearch可能被取代
## 参考
- [PostgreSQL JSON文档](https://www.postgresql.org/docs/current/datatype-json.html)
- [PostgreSQL全文搜索](https://www.postgresql.org/docs/current/textsearch.html)
- 内部:性能基准在`/docs/benchmarks/database-comparison.md`
模板2:轻量级ADR
# ADR-0012: 采用TypeScript进行前端开发
**状态**: 已接受
**日期**: 2024-01-15
**决策者**: @alice, @bob, @charlie
## 背景
我们的React代码库已增长至50多个组件,错误报告增加,涉及prop类型不匹配和未定义错误。PropTypes仅提供运行时检查。
## 决策
采用TypeScript用于所有新前端代码。逐步迁移现有代码。
## 后果
**好**: 编译时捕获类型错误,更好的IDE支持,自我文档化代码。
**坏**: 团队学习曲线,初始速度减慢,构建复杂性增加。
**缓解措施**: TypeScript培训课程,允许逐步采用`allowJs: true`。
模板3: Y-Statement格式
# ADR-0015: API网关选择
在**构建微服务架构**的背景下,
面对**需要集中式API管理、认证和速率限制**,
我们决定选择**Kong网关**
而非**AWS API网关和自定义Nginx解决方案**,
以实现**供应商独立、插件可扩展性和团队对Lua的熟悉度**,
接受**我们需要自己管理Kong基础设施**。
模板4: 弃用ADR
# ADR-0020: 弃用MongoDB,改用PostgreSQL
## 状态
已接受(取代ADR-0003)
## 背景
ADR-0003(2021)因模式灵活性需求选择MongoDB用于用户配置文件存储。自那时以来:
- MongoDB的多文档事务对我们的用例仍有问题
- 我们的模式已稳定并很少更改
- 我们现在从其他服务获得PostgreSQL专业知识
- 维护两个数据库增加运营负担
## 决策
弃用MongoDB并迁移用户配置文件到PostgreSQL。
## 迁移计划
1. **阶段1**(第1-2周):创建PostgreSQL模式,启用双写
2. **阶段2**(第3-4周):回填历史数据,验证一致性
3. **阶段3**(第5周):切换读到PostgreSQL,监控
4. **阶段4**(第6周):移除MongoDB写,停用
## 后果
### 积极
- 单一数据库技术减少运营复杂性
- 用户数据的ACID事务
- 团队可聚焦PostgreSQL专业知识
### 消极
- 迁移努力(约4周)
- 迁移期间数据问题风险
- 失去一些模式灵活性
## 学到的教训
从ADR-0003经验文档化:
- 模式灵活性益处被高估
- 多数据库的运营成本被低估
- 在技术决策中考虑长期维护
模板5: 征求意见(RFC)风格
# RFC-0025: 为订单管理采用事件溯源
## 总结
提议为订单管理领域采用事件溯源模式,以提高可审计性、启用时间查询和支持业务分析。
## 动机
当前挑战:
1. 审计要求需要完整订单历史
2. “在时间X订单状态是什么?”查询不可能
3. 分析团队需要事件流用于实时仪表板
4. 客户支持的订单状态重建是手动的
## 详细设计
### 事件存储
订单创建 { 订单Id, 客户Id, 物品[], 时间戳 } 订单物品添加 { 订单Id, 物品, 时间戳 } 订单物品移除 { 订单Id, 物品Id, 时间戳 } 付款接收 { 订单Id, 金额, 付款Id, 时间戳 } 订单发货 { 订单Id, 跟踪号码, 时间戳 }
### 投影
- **当前订单状态**: 用于查询的物化视图
- **订单历史**: 完整时间线用于审计
- **每日订单指标**: 分析聚合
### 技术
- 事件存储: EventStoreDB(专门构建,处理投影)
- 考虑的替代: Kafka + 自定义投影服务
## 缺点
- 团队学习曲线
- 复杂性增加 vs. CRUD
- 需要精心设计事件(存储后不可变)
- 存储增长(事件从不删除)
## 替代方案
1. **审计表**: 更简单但无法启用时间查询
2. **CDC从现有数据库**: 复杂,不改变数据模型
3. **混合**: 仅事件源用于订单状态更改
## 未解决问题
- [ ] 事件模式版本控制策略
- [ ] 事件保留策略
- [ ] 性能的快照频率
## 实现计划
1. 原型单个订单类型(2周)
2. 团队事件溯源培训(1周)
3. 完全实现和迁移(4周)
4. 监控和优化(持续)
## 参考
- [Martin Fowler的事件溯源](https://martinfowler.com/eaaDev/EventSourcing.html)
- [EventStoreDB文档](https://www.eventstore.com/docs)
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 # 取代0003
ADR索引(README.md)
# 架构决策记录
此目录包含[项目名称]的架构决策记录(ADRs)。
## 索引
| ADR | 标题 | 状态 | 日期 |
|-----|-------|--------|------|
| [0001](0001-use-postgresql.md) | 使用PostgreSQL作为主数据库 | 已接受 | 2024-01-10 |
| [0002](0002-caching-strategy.md) | 缓存策略与Redis | 已接受 | 2024-01-12 |
| [0003](0003-mongodb-user-profiles.md) | MongoDB用于用户配置文件 | 弃用 | 2023-06-15 |
| [0020](0020-deprecate-mongodb.md) | 弃用MongoDB | 已接受 | 2024-01-15 |
## 创建新ADR
1. 复制`template.md`到`NNNN-title-with-dashes.md`
2. 填写模板
3. 提交PR以供审查
4. 批准后更新此索引
## ADR状态
- **提议**: 正在讨论
- **已接受**: 决策已做,正在实施
- **弃用**: 不再相关
- **被取代**: 被另一个ADR取代
- **拒绝**: 考虑过但未采纳
自动化(adr-tools)
# 安装adr-tools
brew install adr-tools
# 初始化ADR目录
adr init docs/adr
# 创建新ADR
adr new "使用PostgreSQL作为主数据库"
# 取代ADR
adr new -s 3 "弃用MongoDB,改用PostgreSQL"
# 生成目录表
adr generate toc > docs/adr/README.md
# 链接相关ADRs
adr link 2 "补充" 1 "被补充"
审查流程
## ADR审查清单
### 提交前
- [ ] 背景清楚解释问题
- [ ] 所有可行选项已考虑
- [ ] 优点/缺点平衡且诚实
- [ ] 后果(积极和消极)已文档化
- [ ] 相关ADRs已链接
### 审查期间
- [ ] 至少2名高级工程师审查
- [ ] 已咨询受影响团队
- [ ] 已考虑安全影响
- [ ] 成本影响已文档化
- [ ] 已评估可逆性
### 接受后
- [ ] ADR索引已更新
- [ ] 团队已通知
- [ ] 实施票证已创建
- [ ] 相关文档已更新
最佳实践
应做
- 尽早编写ADRs - 在实施开始前
- 保持简短 - 最多1-2页
- 诚实对待权衡 - 包括真实缺点
- 链接相关决策 - 构建决策图
- 更新状态 - 弃用时取代
不应做
- 不要更改已接受的ADRs - 编写新的来取代
- 不要跳过背景 - 未来读者需要背景
- 不要隐藏失败 - 拒绝的决策有价值
- 不要含糊 - 具体决策,具体后果
- 不要忘记实施 - 无行动的ADR是浪费