架构决策记录:有效管理与文档化架构决策
1. 接口与构建技术概述
接口涉及服务和组件的访问与编排方式,通常借助网关、集成中心、服务总线或 API 代理来实现。接口一般需要定义契约,包括这些契约的版本控制和弃用策略。接口会影响系统的其他使用者,因此在架构上具有重要意义。
构建技术则是关于平台、框架、工具甚至流程的决策,尽管本质上是技术性的,但可能会对架构的某些方面产生影响。
2. 架构决策记录(ADRs)简介
架构决策记录(ADRs)是记录架构决策的有效方式。它最初由 Michael Nygard 在博客文章中推广,后来在 ThoughtWorks 技术雷达中被标记为“采用”。ADR 是一个简短的文本文件(通常一到两页),用于描述特定的架构决策。它可以用纯文本编写,但通常采用 AsciiDoc 或 Markdown 等文本格式,也可以使用维基页面模板编写。
管理 ADR 也有相应的工具。Nat Pryce 编写了一个名为 ADR - tools 的开源工具,它提供了命令行界面来管理 ADR,包括编号方案、存储位置和替代逻辑。Micha Kops 写了一篇关于使用 ADR - tools 的博客文章,提供了如何使用该工具管理架构决策记录的示例。
3. ADR 的基本结构
ADR 的基本结构包含五个主要部分:标题、状态、上下文、决策和后果。此外,我们还建议添加两个额外部分:合规性和注释。这个基本结构可以根据需要扩展,只要保持模板的一致性和简洁性。例如,必要时可以添加“替代方案”部分,以分析所有可能的替代解决方案。
| 部分 | 描述 |
|---|---|
| 标题 | 通常按顺序编号,包含描述架构决策的简短短语,应清晰描述决策的性质和背景,同时简洁明了。 |
| 状态 | 可以标记为“提议”“接受”或“替代”。“提议”表示决策需由更高级别的决策者或架构治理机构批准;“接受”表示决策已获批准,可实施;“替代”表示决策已被另一个 ADR 更改和替代。 |
| 上下文 | 指定影响决策的因素,描述具体情况或问题,并简要阐述可能的替代方案。这部分也是记录架构的有效方式。 |
| 决策 | 包含架构决策及完整的决策理由。建议使用肯定、明确的表述方式,强调决策的“原因”而非“方式”。 |
| 后果 | 记录架构决策的整体影响,包括好的和坏的方面。这部分也可用于记录与架构决策相关的权衡分析。 |
| 合规性 | 非标准部分,但强烈建议添加。该部分促使架构师考虑如何从合规性角度衡量和管理架构决策,决定合规性检查是手动进行还是使用适应度函数自动化进行。 |
| 注释 | 包含 ADR 的各种元数据,如原作者、批准日期、批准人、替代日期、最后修改日期、修改人、最后修改内容等。 |
4. ADR 各部分详细说明
- 标题 :标题应具有描述性且简洁。例如,“42. 订单服务和支付服务之间使用异步消息传递”,能清晰表明决策内容。
-
状态
- 提议(Proposed) :决策需由更高级别的决策者或架构治理机构(如架构审查委员会)批准。
- 接受(Accepted) :决策已获批准,可进入实施阶段。
-
替代(Superseded)
:决策已被另一个 ADR 更改和替代。替代状态意味着先前的 ADR 状态为“接受”,提议的 ADR 不会被替代,而是持续修改直至被接受。例如:
-
ADR 42. 订单服务和支付服务之间使用异步消息传递
- 状态:被 68 替代
-
ADR 68. 订单服务和支付服务之间使用 REST
- 状态:接受,替代 42
-
ADR 42. 订单服务和支付服务之间使用异步消息传递
- 请求评论(Request for Comments,RFC) :如果架构师希望发布 ADR 草案征求意见,可以创建“请求评论”状态,并指定评论截止日期。这能避免“分析瘫痪”反模式,即决策永远在讨论但无法做出。截止日期到达后,架构师分析评论,调整决策,最终确定状态为“提议”或“接受”。
- 确定 ADR 状态时,架构师需与上级或首席架构师讨论自行批准架构决策的标准。三个重要标准是成本、跨团队影响和安全性。成本包括软件购买或许可费用、额外硬件成本以及实施架构决策的总体工作量。如果决策成本超过一定金额,或影响其他团队、系统或有安全影响,则需由更高级别的治理机构或首席架构师批准。
- 上下文 :该部分描述促使做出决策的情况,允许架构师描述具体问题并简要说明替代方案。例如,“订单服务必须将信息传递给支付服务以支付当前下单的订单,可使用 REST 或异步消息传递”,既说明了场景,也提及了替代方案。
- 决策 :决策部分应使用肯定、明确的表述,如“我们将在服务之间使用异步消息传递”,而不是模糊的表述。该部分强调决策的“原因”,理解决策原因比理解实现方式更重要。例如,几年前决定使用 Google 的远程过程调用(gRPC)在两个服务之间通信,如果后来的架构师不了解当初使用 gRPC 是为了显著降低延迟(尽管会导致服务紧密耦合),而选择使用消息传递来解耦服务,可能会导致延迟显著增加,进而导致上游系统超时。
- 后果 :记录架构决策的整体影响,包括积极和消极方面。这促使架构师思考影响是否超过决策的好处。还可用于记录权衡分析,例如,决定使用异步消息传递发布网站评论以提高响应速度,但可能面临错误处理复杂的问题。通过 ADR 的后果部分记录权衡分析,能提供架构决策的完整背景信息,避免类似问题。
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B{是否需要征求意见?}:::decision
B -->|是| C(设置为请求评论状态):::process
B -->|否| D(设置为提议状态):::process
C --> E(等待评论截止日期):::process
E --> F(分析评论并调整决策):::process
F --> G{能否自行批准?}:::decision
G -->|是| H(设置为接受状态):::process
G -->|否| D
D --> I(提交给上级或治理机构批准):::process
I --> J{是否批准?}:::decision
J -->|是| H
J -->|否| K(修改决策):::process
K --> D
H --> L([结束]):::startend
5. 合规性部分
合规性部分不是 ADR 的标准部分,但强烈建议添加。它促使架构师思考如何从合规性角度衡量和管理架构决策。架构师需决定合规性检查是手动进行还是使用适应度函数自动化进行。如果可以自动化,需在该部分说明适应度函数的编写方式以及是否需要对代码库进行其他更改以衡量合规性。
例如,在传统的 n 层架构中,业务层业务对象使用的所有共享对象将驻留在共享服务层,以隔离和包含共享功能。这个架构决策可以使用 ArchUnit(Java)或 NetArchTest(C#)自动衡量和管理。以下是使用 ArchUnit 在 Java 中的自动化适应度函数测试示例:
@Test
public void shared_services_should_reside_in_services_layer() {
classes().that().areAnnotatedWith(SharedService.class)
.should().resideInAPackage("..services..")
.because("All shared services classes used by business " +
"objects in the business layer should reside in the services " +
"layer to isolate and contain shared logic")
.check(myClasses);
}
这个自动化适应度函数需要编写新的故事来创建新的 Java 注解(
@SharedService
),并将该注解添加到所有共享类中。该部分还需指定测试内容、测试位置、执行方式和时间。
6. 注释部分
注释部分包含 ADR 的各种元数据,如原作者、批准日期、批准人、替代日期、最后修改日期、修改人、最后修改内容等。即使将 ADR 存储在版本控制系统(如 Git)中,这些额外的元信息也很有用,因此建议无论 ADR 如何存储,都添加该部分。
7. ADR 的存储
架构师创建 ADR 后,需要将其存储在某个地方。每个架构决策应有自己的文件或维基页面。一些架构师喜欢将 ADR 与源代码一起存储在 Git 仓库中,这样可以对 ADR 进行版本控制和跟踪。但对于大型组织,我们不建议这样做,原因如下:
- 并非所有需要查看架构决策的人都能访问 Git 仓库。
- 对于应用程序 Git 仓库之外的上下文(如集成架构决策、企业架构决策或适用于每个应用程序的决策),Git 仓库不是一个好的存储位置。
因此,建议将 ADR 存储在维基(使用维基模板)或共享文件服务器上的共享目录中,以便维基或其他文档渲染软件能够轻松访问。存储结构可以如下:
-
应用程序目录
:包含特定应用程序上下文的架构决策。该目录可进一步细分,“通用”子目录包含适用于所有应用程序的决策,其他子目录对应特定应用程序或系统,包含该应用程序或系统的特定决策。
-
集成目录
:包含涉及应用程序、系统或服务之间通信的 ADR。
-
企业目录
:包含影响所有系统和应用程序的全局架构决策。
当使用维基存储 ADR 时,上述结构同样适用,每个目录结构代表一个导航着陆页,每个 ADR 表示为每个导航着陆页内的单个维基页面。公司可以根据自身情况选择合适的目录或着陆页名称,但要确保团队之间的一致性。
8. ADR 作为文档和标准
- 作为文档 :软件架构的文档化一直是个难题,虽然有一些架构绘图标准,但缺乏软件架构文档化的标准。ADR 可以作为有效的软件架构文档方式。上下文部分描述需要做出架构决策的系统特定区域和替代方案;决策部分说明做出特定决策的原因,这是最好的架构文档形式;后果部分描述决策的其他方面,如性能与可扩展性的权衡分析。
- 作为标准 :很少有人喜欢标准,因为标准往往被认为是为了控制人们的行为而存在。使用 ADR 制定标准可以改变这种情况。上下文部分描述促使制定特定标准的情况;决策部分不仅表明标准是什么,更重要的是说明标准存在的原因,这有助于判断标准是否合理;后果部分让架构师思考和记录制定特定标准的影响和后果,从而决定是否应该应用该标准。开发人员越了解标准存在的原因,就越有可能遵守标准。
架构决策记录:有效管理与文档化架构决策
9. ADR 在实际场景中的应用示例
为了更好地理解 ADR 的实际应用,我们来看一个具体的例子。假设一个电商系统需要对订单处理流程进行架构升级,以下是根据 ADR 结构记录该决策的详细内容。
| 部分 | 详情 |
|---|---|
| 标题 | 56. 电商订单处理系统升级为微服务架构 |
| 状态 | 提议。此决策需提交给架构审查委员会进行审批,因为该升级涉及多个团队,且成本较高。 |
| 上下文 | 随着业务的增长,当前的单体订单处理系统变得越来越难以维护和扩展。系统响应时间变长,新功能的开发和部署也变得复杂。替代方案包括继续优化单体架构或升级为微服务架构。 |
| 决策 | 我们将把电商订单处理系统升级为微服务架构。原因是微服务架构可以提高系统的可扩展性、可维护性和响应速度,并且能够更好地支持团队的独立开发和部署。 |
| 后果 | |
| - 积极影响 :提高系统的可扩展性,能够快速应对业务增长;不同团队可以独立开发和部署微服务,提高开发效率;增强系统的容错性,一个微服务的故障不会影响整个系统。 | |
| - 消极影响 :增加了系统的复杂性,需要更多的运维和管理工作;服务之间的通信可能会引入延迟;需要投入更多的资源进行团队培训和技术选型。 | |
| - 权衡分析 :从长远来看,微服务架构带来的好处远远超过了其带来的复杂性和成本。虽然短期内会面临一些挑战,但通过合理的规划和管理,可以有效降低这些风险。 | |
| 合规性 | 可以使用 ArchUnit 编写自动化适应度函数来确保微服务之间的依赖关系符合架构设计。例如,编写测试来验证每个微服务是否只依赖于必要的其他微服务。同时,需要制定相应的代码规范和架构审查流程,确保新的架构决策得到有效执行。 |
| 注释 | |
| - 原作者:张三 | |
| - 批准日期:待确定 | |
| - 批准人:待确定 | |
| - 替代日期:无 | |
| - 最后修改日期:当前日期 | |
| - 修改人:张三 | |
| - 最后修改内容:完善决策理由和后果分析 |
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始升级订单处理系统]):::startend --> B{选择架构方案}:::decision
B -->|单体架构优化| C(继续优化单体架构):::process
B -->|微服务架构升级| D(升级为微服务架构):::process
C --> E(评估优化效果):::process
E --> F{是否满足需求?}:::decision
F -->|是| G(结束):::startend
F -->|否| B
D --> H(进行微服务拆分):::process
H --> I(开发和部署微服务):::process
I --> J(进行集成测试):::process
J --> K{是否通过测试?}:::decision
K -->|是| G
K -->|否| L(调整和修复问题):::process
L --> I
10. ADR 的版本管理和变更跟踪
由于架构决策可能会随着时间和业务需求的变化而改变,因此对 ADR 进行版本管理和变更跟踪非常重要。使用版本控制系统(如 Git)可以有效地管理 ADR 的版本。每次对 ADR 进行修改时,都应该记录详细的修改信息,包括修改日期、修改人、修改内容等。
当一个 ADR 被替代时,需要在新的 ADR 中明确标记替代的 ADR 编号,同时在被替代的 ADR 中标记被替代的状态和替代它的 ADR 编号。这样可以形成一个清晰的历史记录,方便后续的查阅和分析。例如:
- ADR 56. 电商订单处理系统升级为微服务架构
- 状态:替代了 ADR 32(原订单处理系统架构优化方案)
- ADR 32. 原订单处理系统架构优化方案
- 状态:被 56 替代
11. ADR 的团队协作和沟通
ADR 不仅是记录架构决策的工具,也是团队协作和沟通的重要手段。在创建和修改 ADR 的过程中,架构师应该与相关的团队成员(如开发人员、测试人员、项目经理等)进行充分的沟通和交流。
- 征求意见 :在 ADR 处于“请求评论”状态时,广泛征求团队成员的意见和建议。可以通过邮件、会议等方式进行沟通,确保每个人都有机会表达自己的观点。
- 决策共识 :在做出最终决策之前,确保团队成员对决策有充分的理解和共识。如果存在分歧,应该进行深入的讨论和分析,以达成最佳的决策。
- 知识共享 :ADR 可以作为团队的知识共享平台,新加入的成员可以通过查看 ADR 了解系统的架构决策历史和背景信息,快速融入团队。
12. ADR 的持续改进
架构决策不是一次性的过程,而是一个持续改进的过程。随着业务的发展和技术的进步,架构决策可能需要不断地调整和优化。因此,应该定期对 ADR 进行审查和评估,以确保其仍然符合业务需求和技术趋势。
- 定期审查 :可以每月或每季度对 ADR 进行一次审查,检查决策的执行情况和效果。如果发现决策存在问题或需要调整,及时进行修改和更新。
- 反馈机制 :建立有效的反馈机制,鼓励团队成员对架构决策提出改进建议。可以通过设立专门的反馈渠道(如邮件、论坛等)来收集反馈信息。
- 经验总结 :在每次架构决策的实施过程中,总结经验教训,为未来的决策提供参考。可以将这些经验教训记录在 ADR 的注释部分或单独的文档中。
13. 总结
架构决策记录(ADRs)是一种非常有效的管理和文档化架构决策的方法。通过使用 ADR,架构师可以清晰地记录架构决策的背景、原因、影响和后果,为团队提供了一个透明和可追溯的决策过程。同时,ADR 还可以作为软件架构的文档和标准,帮助开发人员更好地理解和遵守架构设计。
在实际应用中,应该根据团队的规模和业务需求选择合适的 ADR 存储方式和管理工具。同时,要注重 ADR 的团队协作和沟通,确保决策的共识和执行。最后,要建立持续改进的机制,不断优化架构决策,以适应业务的发展和技术的进步。
希望本文能够帮助你更好地理解和应用 ADR,提高架构决策的质量和效率。如果你在使用 ADR 的过程中遇到任何问题或有任何建议,欢迎在评论区留言讨论。
超级会员免费看

被折叠的 条评论
为什么被折叠?



