软件架构概述

11 min入门

软件架构定义、架构师角色、架构决策与架构文档。

1. 从”盖楼”说起:什么是软件架构

1.1 一个楼的故事

想象你要盖一栋 30 层的写字楼。开工前,谁会先决定这些事?

  • 承重墙放在哪里?每层能承受多重的荷载?
  • 电梯井预留几个?消防通道怎么设计?
  • 楼是框架结构还是剪力墙结构?
  • 以后能不能在楼顶加两层?能不能改造成住宅?

这些决定,都是建筑设计师在画施工图之前就要拍板的。它们的共同特征是:

  • 决定整栋楼的命运:选错了结构,后面装修得再好也白搭
  • 一旦定下,极难更改:楼盖到 10 层才发现承重墙位置错了,只能拆了重建
  • 影响所有后续工作:水电、装修、幕墙都要在结构确定之后才能做

软件架构(Software Architecture)就是软件的”建筑结构设计”。它是在写具体代码之前,对系统”整体怎么组织”做出的一批高层决策。

1.2 软件架构的定义

软件架构的权威定义(来自 SEI 卡内基梅隆软件工程研究所):

软件架构 = 系统的一组重要结构决策,包括系统的组件、组件之间的关系、组件与环境的交互方式,以及指导系统设计与演化的原则。

拆开理解这句话,包含四个要素:

要素含义举例
组件系统的构建块用户模块、订单模块、数据库
连接器组件间的交互机制HTTP 调用、消息队列、函数调用
约束组件使用的规则“业务层不能直接访问数据库”
配置组件和连接器的组合部署在 3 台服务器、数据库做主从

1.3 架构 vs 设计:它们有什么不同

初学者最容易混淆的就是”架构”和”设计”。用一个简单区分:

架构回答”系统由哪些大块组成、大块之间怎么配合”;设计回答”每个大块内部怎么实现”。

维度架构(Architecture)设计(Design)
层级系统级模块/类级
关注全局结构与交互局部实现细节
变更成本极高(牵一发动全身)较低(局部修改)
影响范围整个系统单个模块
谁来做架构师 + 团队共识每个开发者
类比楼的结构设计房间的装修风格

关键认知:架构决策和设计决策没有绝对清晰的边界,但有一条实用判断标准——这个决定改起来贵不贵?贵(影响全系统)就是架构决策,便宜(影响局部)就是设计决策。

2. 架构的核心要素

2.1 组件:系统的”积木”

组件是构成系统的基本单元。一个组件通常具有:

  • 清晰的职责:只做一类事(如”负责用户认证”)
  • 可识别边界:能明确说出”这个组件负责什么、不负责什么”
  • 可替换性:理论上可以换一个实现(比如换个数据库驱动)

2.2 连接器:组件间的”管道”

组件之间的交互方式,决定了系统的很多特性:

连接器类型特点适用场景
直接函数调用最快、最简单同进程内模块
HTTP/REST跨网络、标准统一服务间调用
消息队列异步、解耦削峰填谷、系统解耦
数据库共享数据共享强一致需求
事件总线广播、松耦合事件驱动架构

一个重要的架构教训:连接器的选择往往比组件的实现更影响系统质量。比如”支付成功后发通知”用同步调用还是消息队列,决定了系统在支付服务故障时的表现。

2.3 约束与配置

约束是”不能做什么”的规则,比如:

  • 业务层不得直接依赖具体数据库实现
  • 所有外部输入必须经过校验
  • 模块间禁止循环依赖

配置是”系统如何部署组合”,比如:

  • 3 个 Web 实例 + 1 个数据库主节点 + 2 个从节点
  • 缓存层用 Redis,消息用 Kafka

3. 架构师:系统命运的关键角色

3.1 架构师在做什么

架构师不是”职位最高的工程师”,而是专门为系统长远质量负责的人。他的核心职责是:

职责说明日常表现
技术决策选择技术栈和架构风格“用微服务还是单体?用 MySQL 还是 PG?”
质量保障确保系统满足质量属性“这样设计可用性够吗?性能达标吗?”
风险管理识别和缓解技术风险“这个方案依赖某个闭源库,风险大吗?”
沟通桥梁连接业务和技术把业务需求翻译成技术方案
技术指导制定技术标准和规范编码规范、评审清单
知识传承培养团队技术能力技术分享、方案评审

3.2 架构师的三个”不能”

不能一:不能只做”画图的人”。 画了架构图然后交给团队实现、自己不管——架构图会很快与现实脱节。好的架构师要持续跟进实现,确保架构决策落地。

不能二:不能当”独裁者”。 架构是团队的集体决策,不是某个人的意志。好的架构师会倾听各方意见,用数据说话,而不是”我说了算”。

不能三:不能做”完美主义者”。 追求”理想架构”而拒绝妥协,会导致系统永远上不了线。好的架构师懂得在”理想”和”现实约束”之间取得平衡。

3.3 架构师的能力模型

flowchart TD
    A[架构师能力模型]
    A --> TD[技术深度<br/>对核心领域的深入理解]
    A --> TB[技术广度<br/>跨领域的技术视野]
    A --> BU[业务理解<br/>把业务需求转化为技术方案]
    A --> COM[沟通能力<br/>与不同角色有效沟通]
    A --> DEC[决策能力<br/>在不确定性下做出决策]
    A --> LDR[领导力<br/>引导团队走向正确方向]

4. 架构决策:架构师最重要的产出

4.1 什么是架构决策

架构决策(Architectural Decision)是那些影响深远、难以逆转、代价高昂的技术选择。例如:

  • 选用哪种数据库
  • 采用微服务还是单体
  • 用消息队列还是直接调用
  • 前端用什么框架

4.2 为什么架构决策必须被记录

想象半年后的场景:新来的工程师看着系统问”为什么我们用 Kafka 而不用 RabbitMQ?“——没有人能回答,因为当年做决定的架构师已经离职了。于是新团队不敢动这块,或者干脆推翻重来。

记录架构决策(ADR,Architecture Decision Record) 就是为了解决这个问题:把”当时的背景、做的决定、为什么这么做、代价是什么”写下来,让未来的团队能理解并延续决策。

核心原则:决策可以被推翻,但必须被记录。记录的价值不是”这个决策永远正确”,而是”后人能看到决策的来龙去脉”。

4.3 决策的五大原则

原则含义实操
延迟决策不着急的决定不要提前做“这个问题下季度再定”
可逆性优先优先选可逆的决策能换的组件优于绑死的
证据驱动基于数据而非直觉先做 PoC 验证再拍板
最简可行选最简单的可行方案不要一上来就上最重的方案
适度设计满足当前需求,预留空间不超前设计,也不裸奔

4.4 ADR 示例

# ADR-001: 选择微服务架构

## 状态

已接受

## 背景

单体应用已无法满足业务快速增长的需求:
- 30 人的团队在同一代码库协作,合并冲突频繁
- 每次发布全量回归,发布周期从 1 天拖到 2 周
- 某个模块的高负载会拖垮整个系统

## 决策

采用微服务架构,按业务域拆分服务(用户、订单、商品、支付)。

## 理由

1. 独立部署:各服务可独立发布,不再相互阻塞
2. 技术异构:各服务可选择合适技术栈
3. 团队自治:每个团队负责自己的服务
4. 弹性伸缩:按需扩展高负载服务

## 后果

- 增加运维复杂度(服务发现、监控、部署)
- 需要 API 网关与分布式事务方案
- 需要更强的可观测性体系

## 替代方案

- 模块化单体:改造成本更低,但部署仍整体化
- 继续单体 + 扩展机器:无法解决协作与发布问题

5. 架构文档:让架构可理解、可维护

5.1 为什么需要架构文档

架构是”系统的骨架”,但骨架是看不见的。架构文档的作用是让看不见的骨架变得可见:

  • 新成员通过文档快速理解系统(而不是自己翻三个月代码)
  • 团队通过文档对齐认知(避免”我以为的架构”和”实际架构”不一致)
  • 评审通过文档评估决策(架构评审的对象)

5.2 C4 模型:自顶向下的四层视图

业界最流行的架构文档模型是 C4 模型(Context, Containers, Components, Code),它用四个由粗到细的层次描述系统:

层级视图描述受众
Level 1系统上下文图系统与外部的关系所有人
Level 2容器图系统由哪些容器组成开发者/架构师
Level 3组件图每个容器内的组件开发者
Level 4代码图组件的内部实现开发者

为什么用 C4:很多人画架构图只会画”一堆框框和箭头”,C4 提供了清晰的层级:先画”系统和外部的边界”(Level 1),再画”系统里有哪些进程/服务”(Level 2),再往下钻。它防止了”一上来就画底层细节”的通病。

5.3 架构视图(4+1 视图模型)

除了 C4,经典教材还常用”4+1 视图”来描述架构的不同侧面:

视图描述关注者
逻辑视图系统的功能分解终端用户
进程视图并发与同步集成者
开发视图代码的组织方式程序员
物理视图部署拓扑运维人员
场景(+1)关键用例贯穿上述视图所有人

架构文档的黄金法则:一个系统需要多张图,因为不同的人关心不同的侧面。一张图试图画下所有信息,结果是谁都看不懂。

6. 架构反模式:常见的大坑

理解”不该怎么做”往往比”该怎么做”更重要。以下是架构领域最常见的五个反模式:

6.1 大泥球(Big Ball of Mud)

症状:没有清晰的结构,代码相互纠缠,改一处崩三处。模块边界形同虚设,任何人都在任何地方加代码。

成因:架构设计缺失 + 长期缺乏重构。

对策:从最容易的部分开始逐步分层,先画出”实际架构”,再逐步向”目标架构”收敛。

6.2 过度工程(Over-Engineering)

症状:为”未来可能的需求”引入了分布式、微服务、复杂的抽象层——但当前业务一个简单单体就够。

成因:架构师焦虑”以后扩展不了怎么办”。

对策:YAGNI(You Aren’t Gonna Need It)——不为不存在的需求设计。架构应该”为已知的需求做充分设计,为未知的需求留简单空间”。

6.3 复制-粘贴架构

症状:多个服务的代码结构完全一样(同一个模板生成),只是改了名字。任何修改要在 N 个地方重复做。

成因:缺乏统一平台,团队各自为政。

对策:提取共享库/平台,或接受”标准化模板 + 自动化生成”。

6.4 供应商锁定(Vendor Lock-in)

症状:整个系统深度绑定某个云厂商/闭源产品的专有特性,迁移成本高到不可承受。

成因:选型时只看”现在好用”,不看”未来可换”。

对策:关键依赖增加抽象层(但不要过度抽象)、评估迁移成本、避免使用非必要专有特性。

6.5 架构黑洞

症状:架构文档写得漂亮,但实际代码早就偏离了架构。架构”看起来存在,实际上不存在”。

成因:架构决策不落地、实现无人跟踪、文档与代码脱节。

对策:架构评审要对照真实代码;架构变更要同步更新文档;用工具(如依赖检查、架构测试)自动校验架构符合性。

7. 架构演进:架构不是一次定死的

7.1 架构是”演化的”,不是”设计的”

很多初学者以为架构是”设计好就固定了”。实际上,架构和软件一样需要演化:

  • 业务变了 → 架构要跟着变
  • 技术栈成熟了 → 可以考虑替换
  • 团队规模变了 → 协作方式要变

现代架构思维:不是”设计一个最终架构”,而是”设计一个能演化的架构”。就像盖楼时预留电梯井和管道位置——你不知道未来要装什么设备,但你知道要为未来的改造留空间。

7.2 演进原则

  1. 小步演进:架构调整要分阶段,每次保持系统可用
  2. 演进有记录:每次架构变化都有 ADR 记录理由
  3. 对照验证:定期检查”实际架构 vs 文档架构”是否一致
  4. 权衡明确:每次演进都要说清楚”得到了什么、放弃了什么”