跳到内容

3.2 架构视图、质量权衡与决策记录

架构不是方框数量,也不是技术名词清单。它是一组对系统结构、关键质量属性和高成本选择有长期影响的决策。架构文档的任务,是让不同读者看到与自己关注点匹配的视图,并理解这些决定为何成立。

从架构重要需求出发

不是每条需求都影响架构。通常需要优先处理:

  • 决定系统边界或主要职责分配的功能;
  • 可用性、性能、安全、合规等关键质量属性场景;
  • 无法轻易改变的技术与组织约束;
  • 风险高、未知多或修改代价大的决定。

例如,“报名页面按钮颜色”通常不改变架构;“支付确认必须可审计且不能因消息重投重复入账”会影响数据模型、幂等策略和系统边界。

C4:用不同缩放层级解释静态结构

C4 的核心静态结构图有四层:System Context、Container、Component 和 Code。这里的 Container 不是专指 Docker 容器,而是可独立运行或部署的数据存储、应用、服务等单元。

层级回答的问题主要读者
System Context系统服务谁,与哪些外部系统交互?所有利益相关者
Container系统由哪些可运行/存储单元组成,怎样通信?技术人员与相关业务方
Component某个容器内部有哪些主要职责?开发团队
Code组件怎样落到类、接口或模块?具体实现者

不必为了“完整”画满四层。C4 官方说明中,Context 和 Container 对多数团队已经足够;Component 与 Code 只在能回答实际问题时添加。

一个擂台系统的 Context 视图可以写成:

text
[参赛者] ----报名/取消/查赛程----> [擂台系统]
[管理员] ----建赛事/裁决/审计----> [擂台系统]
[擂台系统] ----收退款请求--------> [支付平台]
[擂台系统] ----发送通知----------> [消息供应商]

进入 Container 视图后才讨论内部单元:

text
[Web 应用]
    -> HTTPS/JSON -> [业务应用]
                         -> SQL -> [关系数据库]
                         -> publish -> [消息代理]
    [通知 Worker] <- consume --------+
          -> HTTPS -> [消息供应商]

每个框应标明名称、类型/技术和职责;每条关系标明方向、目的与协议。只有箭头而没有语义,读者无法判断它是同步调用、异步事件还是数据依赖。

UML:按问题选择图,不按清单交作业

UML 是一套标准化建模语言,不要求项目使用所有图。常见选择是:

  • 类图:领域结构、关键关系和静态依赖;
  • 时序图:一次场景中的调用顺序、边界与失败路径;
  • 状态机图:实体生命周期和合法转换;
  • 部署图:运行节点与制品的映射。

状态机尤其适合报名流程:

text
PENDING --支付成功--> CONFIRMED --用户取消--> CANCELLED
   |                       |
   +--超时-------------> EXPIRED
                           +--赛事结束--> COMPLETED

这张图还不完整:团队必须明确重复事件、非法转换和并发转换的处理。图的价值在于逼出问题,不能替代文字契约。

时序图应包含重要失败路径,而不只画“阳光路径”。例如支付成功后写库失败,究竟由回调重试、对账任务还是人工流程恢复,会直接影响一致性设计。

4+1:按利益相关者关注点组织视图

Kruchten 的 4+1 模型用多个并行视图描述架构:

视图重点
Logical面向功能的主要设计元素与关系
Process运行期进程、并发、通信与同步
Development源码模块、子系统和开发组织
Physical软件到硬件/运行节点的部署映射
Scenarios(+1)用关键用例驱动并验证其他视图

C4 与 4+1 不是一一对应的替代品。C4 主要提供层级化结构表达,4+1 强调不同关注点;项目可以用 C4 Container 图解释静态单元,再用时序图解释进程交互、用部署图解释物理视图。

架构图必须绑定质量属性

图上增加缓存、消息代理或副本,并不会自动获得高性能与高可用。每个结构选择都应回到场景:

text
需求:决赛期间排名查询 p95 < 300 ms。
候选:每次实时聚合;维护预计算排名;短 TTL 缓存。
代价:新鲜度、写放大、失效复杂度、额外运维组件。
验证:在约定并发和数据规模下做负载测试,并观测尾延迟。

架构设计是在约束下分配责任和风险,不是从模式目录中挑名字。

用 ADR 保存“为什么”

图通常展示当前结构,却很难解释为什么没有选另一个方案。Architecture Decision Record(ADR)适合记录单个重要决定:

markdown
# ADR-0003:首个版本采用模块化单体

状态:Accepted

## Context
团队规模、交付期限、关键质量属性、已知增长假设。

## Options
1. 模块化单体
2. 独立微服务

## Decision
选择模块化单体,并以明确模块 API 隔离报名、赛程和结算。

## Consequences
- 本地事务和部署流程更简单;
- 模块不能独立扩缩容;
- 若增长假设失效,按监控数据重新评估边界。

ADR 应记录上下文、备选方案、决定、理由和后果。已接受的决定发生变化时,新增一条 Superseded 记录并互相链接,比悄悄改写历史更容易追溯。

只记录对结构、关键质量属性或可逆性有显著影响的决定。“使用四个空格缩进”更适合代码规范。

让文档保持可用

  • 图上注明标题、范围、受众、更新时间和图例;
  • 让名称与代码、部署和监控中的名称一致;
  • 文档与代码放在同一评审流程,变更结构时同步更新;
  • 删除过期图,或明确标记为历史快照;
  • 用关键场景走查图:正常路径、超时、重试、部分故障和恢复;
  • 对低置信度决策写出验证方法与复审触发条件。

本章交付练习

为一个自己熟悉的系统完成:

  1. 一张 System Context 图;
  2. 一张 Container 图;
  3. 一个包含失败路径的时序图或状态机图;
  4. 两条可度量的质量属性场景;
  5. 一份记录真实权衡的 ADR。

如果某个框、箭头或组件无法关联到需求、质量属性或约束,先问它是否真的需要出现在架构中。

参考资料

Built with VitePress | Software Systems Atlas