软件架构部分
本文基于英文讲义,其中某些内容由本人自行翻译。由于时间原因,极少数词汇可能并没有采用业界推荐或常用的译法,而是进行直译。因此,如有必要请参照英文原文。
软件架构概述
什么是软件架构 Software Architecture
?
软件架构设计是软件设计的一部分,是一种高层次的设计活动,同时也是一系列的设计决策。设计者通过将需求、约束、利益相关者、组织结构等方面迭代式地进行综合考虑与决策,最终形成详细的架构设计方案(文档)。架构设计提供了一个更为抽象的整体视角,隐去了实现细节和复杂性,并体现出一种设计理念。因此,软件架构设计可以在满足种种功能需求的同时顾全大局,兼顾非功能需求的实现。软件架构设计的目标是创建一个高质量的软件系统。

架构设计过程
软件架构还可以从多个视角进行分析和描述。
- 逻辑视图
Logical View
:描述架构中具有重要意义的元素及其之间的关系。 - 过程视图
Process View
:描述架构中各元素的并发性和通信机制。 - 物理视图
Physical View
:描述主要过程和组件如何映射到应用程序硬件上。 - 开发视图
Development View
:捕获软件组件的内部组织结构,例如配置管理工具中的组织方式。 - 架构用例
Architecture Use Case
:捕获架构的需求;与多个特定视图相关联。
实际上,架构本身就是一组设计决策的集合,可以将这些决策分为以下几类:
- 职责分配
Allocation of responsibilities
- 协调模型
Coordination model
- 数据模型
Data model
- 资源管理
Management of resources
- 架构元素间的映射
Mapping among architecture elements
- 绑定时机的决策
Binding time decisions
- 技术选型
Choice of technology
软件架构是为实现需求的最早的一批设计决策,它代表着那些最难改变、最应仔细考虑的设计决策。架构设计不仅决定能否以预期的方式满足需求(包括功能和肺功能需求),还会影响开发的投入、成本与风险,甚至可以指导框架的维护和修改。另外,好的设计是可重用的,架构设计可以为后续的系统开发提供一个良好的基础,尤其是为整个产品线的建立和演进提供支持。
需求与质量属性
需求分为功能性需求和非功能性需求(即质量属性)。此外,架构设计还需要考虑约束等其他关注点。
功能性需求是指系统需要完成的具体功能或任务,描述了系统的具体行为,强调如何为利益相关者提供价值。它们通常包括用户故事、用例或功能规格等形式。功能性需求很大程度上与架构相独立——即使是最简单的单体架构也能满足很多功能性需求。
非功能性需求(
约束是指在架构设计过程中需要严格遵守的限制条件或规则,是预先指定的设计决策。约束可以是技术性的(如编程语言、框架、平台等),也可以是业务性的(如法律法规、商业模式等)。它们通常也会影响架构设计的选择和实现方式。
质量属性
质量属性是架构设计中十分重要的一环,在做任何设计决策时都需要考虑。质量属性通常有两大类:
Observable/External
可观测的质量属性 :指系统满足其行为上需求的程度或能力。它们通常可以通过测试或监控来评估,例如性能、可用性、可靠性、安全性等。Not observable/Internal
不可观测的质量属性:指系统被维护、集成与测试的难易程度。它们通常与系统的内部结构和设计有关,例如可维护性、可扩展性、可重用性等。
通常来说,在开发计划中,功能性需求会被优先考虑,但是这种情况下系统经常会因为无法满足质量属性而被重新设计。因此,架构设计阶段就是最适合去解决质量属性问题的阶段。当然,没有质量属性可以完全孤立地存在于设计、实现或部署中,它们是一以贯之的。
质量属性的清晰定义同样重要,它们可以帮助在架构层面上评估设计。通常使用质量属性场景(
- 刺激
Stimulus
:触发系统行为的事件或条件。当它发生时,系统需要做出响应。 - 刺激源
Source (of Stimulus)
:激发刺激的来源,通常是一个实体(如用户、系统本身等)。 - 响应
Response
:刺激到来后,系统对刺激的反应或行为。 - 响应度量
Response Measure
:衡量系统响应的标准或指标,通常是一个量化的值(如时间、资源消耗等)以便进行测试。 - 环境
Environment
:当刺激发生时,系统所处的情境或上下文,例如:正常运行、过载、宕机等。 - 产物
Artifact
:质量属性针对的具体产品或系统/子系统。

质量属性场景框架
若要达到某个质量属性的目标,通常需要在架构设计中做出相应的设计决策。一些重复出现的设计决策可以被称为战术(
下面介绍了常见质量属性的具体场景示例:
质量属性 | 刺激源 | 刺激 | 工件 | 环境 | 响应 | 响应度量 |
---|---|---|---|---|---|---|
Availability 可用性 | Heartbeat 监视器 | 服务器无响应 | 处理器 | 正常操作 | 通知操作者继续操作 | 没有停机时间 |
Interoperability 互操作性 | 车辆信息系统 | 发送当前位置 | 路况监控系统 | 系统运行前已知 | 路况监控结合当前位置和 Google 地图上的其他信息,并且进行汇总 | 我们的信息在 99.9% 的时间是正确的 |
Modifiability 可修改性 | 开发者 | 希望修改 UI 界面 | 代码 | 设计时 | 进行修改和单元测试 | 在 3 个小时内完成 |
Performance 性能 | 用户 | 发起事务 | 系统 | 正常操作 | 事务被处理 | 平均延迟不超过 2 秒 |
Security 安全性 | 来自偏远地区心怀不满的员工 | 尝试修改支付率 | 系统内数据 | 正常操作 | 系统存储修改追踪 | 正确数据在 1 天内恢复储并且进行篡改身份识别 |
Testability 可测试性 | 单元测试者 | 单元测试完成 | 单元测试代码 | 开发时 | 记录结果 | 3 小时内达到 85% 的路径覆盖 |
Usability 易用性 | 用户 | 下载新应用 | 系统 | 运行时 | 用户高效地使用应用 | 在 2 分钟以内的实验 |
以下是常见质量属性的战术板:
质量属性 | 战术 |
---|---|
Availability 可用性 | 检测故障 Detect Faults • Ping/Echo: 定期发送心跳包检测组件是否响应 • 监控 (Monitor): 实时监控系统性能指标 • 心跳 (Heartbeat): 组件间定期发送存活信号 故障恢复 Recover from Faults • 主动冗余 (Active Redundancy): 多个组件同时运行处理相同任务 • 被动冗余 (Passive Redundancy): 备用组件在主组件失效时接管 • 备用 (Spare): 保持备用资源随时可用 • 异常处理 (Exception Handling): 优雅处理预期的异常情况 • 影子状态 (Shadow): 让修复的组件在后台同步状态 • 状态重同步: 确保组件状态一致性 • 扩容重启 (Escalating Restart): 逐步重启更大范围的组件 预防故障 Prevent Faults • 从服务中移除: 主动隔离有问题的组件 • 事务处理: 确保操作的原子性 • 流量管控: 防止系统过载 • 预测模型: 基于历史数据预测潜在故障 |
Interoperability 互操作性 | 定位 • 发现服务:通过搜索已知的目录服务来定位服务。可利用多层间接寻址。 管理接口 • 编排: 使用控制机制来协调、管理和排序特定服务的调用。 • 裁剪接口: 为接口添加或移除功能。 |
Modifiability 可修改性 | • 拆分模块:如果被修改的模块包含大量功能,修改成本可能会很高。 • 增强语义内聚性:如果模块中的职责A和B不服务于相同的目的,应该通过创建新模块或将职责移动到现有模块来将它们放置在不同的模块中。 • 封装为模块引入显式接口,并降低对一个模块的更改传播到其他模块的概率。 • 使用中介者打破依赖关系。 • 当两个模块受到相同更改影响时,对他们重构。 • 延迟绑定:在生命周期的不同阶段绑定某些参数的值,而不是在它们最初定义时绑定。 |
Performance 性能 | 需求侧优化 • 管理采样率(降低采样频率) • 限制事件响应:当离散事件到达系统的速度过快无法处理时,这些事件必须被排队等待处理 • 如果并非所有事件都同等重要,则对事件进行优先级排序 • 通过使用中介者来增加处理事件流的资源,从而减少开销 资源侧优化 • 增加资源(更快的处理器、额外内存、更快的网络等) • 如果请求可以并行处理,则引入并发性 • 维护计算的多个副本:使用负载均衡器将新工作分配给可用的副本服务器之一 • 维护数据的多个副本:利用缓存或数据复制 |
Security 安全性 | • 身份验证 Authentication :确保用户或系统的身份。• 授权 Authorization :控制用户或系统对资源的访问权限。• 加密 Encryption :保护数据在传输和存储过程中的安全性。• 审计日志 Audit Logging :记录系统操作以便于事后审计和追踪。 |
Testability 可测试性 | • 系统状态的控制与观测: 包括建立机制来维护、控制和获取系统状态信息,具备记录/回放功能以重现故障状态,并使用沙箱环境隔离系统进行安全实验。 • 复杂性的限制与管理: 通过减少结构复杂性来降低测试难度。例如减少组件间依赖关系和外部环境依赖、限制类继承的数量和深度、控制多态性和动态调用的使用等。通过限制非确定性来降低行为复杂性,使系统行为更可预测和可测试。 |
Usability 易用性 | • 用户主导支持:系统应该响应用户的操作需求,提供取消、撤销、暂停/恢复等基本控制功能,并能够将相关操作对象聚合处理,让用户在操作过程中保持主动权和灵活性。 • 系统智能支持:系统需要建立并维护任务模型、用户模型和系统模型,通过理解操作上下文、用户知识水平和系统预期行为,为用户提供个性化的智能辅助和反馈,提升整体交互体验。 |
质量属性中一些需要关注的地方
- 可以使用
和 来量化可用性。在计算可用性时通常不考虑计划内停机时间。 (Mean Time Between Failures)表示平均故障间隔时间,是系统在两次故障之间的平均运行时间。 (Mean Time To Repair)表示平均修复时间,是系统在发生故障后恢复正常运行所需的平均时间。- 可用率可以通过以下公式计算:
- 故障管理存在层次关系:故障点
fault
(根本原因)→ 错误error
(中间状态)→ 故障failure
(可观察的系统失效)→ 停机时间outage
- 互操作性中的两大重要方面:发现和响应处理。发现指使用服务者必须能够发现服务的位置、身份和接口;响应处理指服务端可以将响应正确地发送给相关方(如请求者、其他服务、广播等)。
- 并非总要在可修改性上做得面面俱到。这当中需要考虑为修改做准备和做出修改之间的
trade-off
。 - 经常通过响应时间
response time
和吞吐量throughput
来衡量性能。响应时间通常有两个主要影响因素:处理时间processing time
(系统处理请求所需的时间)和排队/阻塞时间queuing/blocked time
(请求在队列中等待处理的时间)。吞吐量是指单位时间内系统能够处理的请求数量。 - 安全性的三原则
CIA
:- 机密性
Confidentiality
:确保信息只能被授权的用户访问。 - 完整性
Integrity
:确保信息在存储和传输过程中不被未经授权的修改。 - 可用性
Availability
:确保授权用户可以在需要时访问信息。
- 机密性
X-ability
在软件架构设计中,很多质量属性都以 -ability
结尾,这些属性通常被称为 X-ability。它们是对系统在特定方面的能力或特性进行描述的术语。除了上面介绍的质量属性之外,还有一些其他常见的 X-ability 属性:Deployability
(可部署性)、Scalability
(可扩展性)、Portability
(可移植性)、Configurability
(可配置性)等。这些属性通常与系统的设计、实现和运维密切相关。
架构模式
架构模式是指能够应用于反复出现的设计问题的一系列架构设计决策。架构模式为上下文、问题和解决方案提供了一种通用的关系框架。架构模式是在实践中不断演化和总结的。与领域特定软件架构(
- 上下文
Context
:一种反复出现的常见情境或环境,通常引发了某个问题。 - 问题
Problem
:在特定上下文中需要解决的设计问题或挑战。 - 解决方案
Solution
:对该问题的经过适当抽象的成功架构解决方案。解决方案通常由元素elements
、关系relations
和约束constraints
组成。
识别架构攸关需求
架构攸关需求(
那么,如何识别这些架构攸关需求呢?通常可以通过以下方式:
- 需求文档:在需求文档中寻找那些对系统架构有重大影响的需求,尤其是那些涉及多个组件或模块之间交互的需求。
- 质量属性创意工坊
Quality Attribute Workshop
:与利益相关者一起讨论和识别(可用采访、pre、头脑风暴等方式)系统的质量属性需求,确定哪些是架构攸关的。 - 效用树
Utility Tree
:利用效用树来组织和按优先级划分质量属性需求。 - 基于人物小传的方法
Persona-based Approach
:通过创建人物小传(人物角色)来识别和分析用户需求,确定哪些需求对架构有重大影响。
下面总结一些常见的架构模式:
模式类型 | 模式名称 | 描述 | 优点 | 缺点 | 应用 |
---|---|---|---|---|---|
模块化模式 | Layered Pattern 分层模式 | 将系统组织成层次结构,每层只能与相邻层通信,高层使用低层的服务 | 关注点分离清晰;易于维护和修改;可重用性高;便于测试和调试 | 性能开销较大;层次过多时复杂度增加;有时会导致不必要的间接调用 | 经典的企业应用架构,如OSI网络模型、MVC中的分层结构 |
组件-连接器模式 | Microkernel Pattern 微内核模式 | 核心系统提供最小功能,其他功能通过插件扩展实现 | 高度可扩展;功能模块化;便于第三方集成;系统稳定性好 | 核心设计复杂;插件间通信复杂;性能可能受插件影响 | 常见于操作系统、IDE、浏览器等需要扩展功能的系统 |
组件-连接器模式 | Broker Pattern 中继器模式 | 通过中介组件协调分布式组件间的通信,解耦客户端和服务端 | 位置透明性;服务动态发现;负载均衡;故障恢复能力强 | 单点故障风险;性能瓶颈;复杂性增加 | 分布式系统中广泛使用,如CORBA、Web服务、消息队列 |
组件-连接器模式 | MVC Pattern MVC模式 | 将应用分为模型(Model)、视图(View)、控制器(Controller)三个组件 | 关注点分离;支持多视图;便于并行开发;易于维护 | 复杂度增加;小型应用可能过度设计;组件间依赖关系复杂 | Web开发的经典模式,衍生出MVP、MVVM等变体 |
组件-连接器模式 | Pipe-and-Filter Pattern 管道-过滤器模式 | 数据流经一系列过滤器组件,每个过滤器执行特定的数据转换 | 可重用性高;并发处理能力强;易于理解和维护;支持增量开发 | 不适合交互式应用;共享状态困难;错误处理复杂 | Unix命令行工具、编译器、数据处理管道中常用 |
组件-连接器模式 | Client-Server Pattern 客户端-服务器模式 | 客户端请求服务,服务器提供服务和资源,明确分工 | 集中管理;资源共享;安全性好;可扩展性强 | 服务器单点故障;网络依赖;可能成为性能瓶颈 | 最常见的分布式架构模式,Web应用、数据库系统的基础 |
组件-连接器模式 | Peer-to-Peer Pattern P2P模式 | 每个节点既是客户端又是服务器,节点间直接通信 | 无单点故障;高可用性;资源利用率高;成本低 | 安全性难以保证;数据一致性复杂;网络拓扑动态变化 | 文件共享系统(BitTorrent)、区块链、分布式计算中应用 |
组件-连接器模式 | Service-Oriented Architecture Pattern SOA模式 | 通过标准接口提供松耦合的服务,服务可被不同应用重用 | 服务重用性高;平台无关;松耦合;业务灵活性强 | 性能开销;复杂的服务治理;标准化要求高 | 企业级应用集成的主流架构,微服务架构的前身 |
组件-连接器模式 | Publisher-Subscriber Pattern 发布者-订阅者模式 | 发布者产生事件,订阅者接收感兴趣的事件,通过事件总线解耦 | 松耦合;动态订阅;可扩展性强;支持一对多通信 | 复杂的事件管理;难以保证消息传递;调试困难 | 消息队列系统、事件驱动架构、GUI框架中广泛应用 |
组件-连接器模式 | Shared-Data Pattern 共享数据模式 | 多个组件通过共享数据存储进行通信和协作 | 数据一致性;持久化存储;简单直观;便于数据分析 | 性能瓶颈;并发控制复杂;组件耦合度高 | 数据库驱动的应用、数据仓库、知识管理系统 |
分配模式 | Map-Reduce Pattern Map-Reduce模式 | 将大数据集分解为小块并行处理(Map),然后合并结果(Reduce) | 高度并行化;可处理大数据;容错性强;自动负载均衡 | 不适合实时处理;编程模型受限;中间数据存储开销大 | 大数据处理的核心模式,Hadoop、Spark等框架的基础 |
分配模式 | Multitier Pattern 多层模式 | 将应用逻辑分布在多个物理层上,如表示层、业务层、数据层 | 可扩展性强;关注点分离;便于维护;支持负载分布 | 网络延迟;复杂的部署;层间通信开销 | 企业级Web应用的标准架构,如三层架构、N层架构 |
其中,模式类型的含义大致如下:
- 模块化模式
Modular Pattern
:将系统分解为多个模块或层次结构,每个模块或层次负责特定的功能或职责。特指开发时需要关注的部分。 - 组件-连接器模式
Component-Connector Pattern
:将系统分解为多个组件和连接器,组件负责处理数据和逻辑,连接器负责组件间的通信。特指运行时的动态表现。 - 分配模式
Allocation Pattern
:将系统映射到物理资源上,描述它们如何在硬件或网络上分布。
Patterns Versus Tactics
架构模式和战术都是软件架构设计的主要工具。战术相当于“砖块”,结构和机制都较为简单,用于实现特定的质量属性或解决特定问题。而架构模式则是将诸多设计决策打包,因此一个架构模式通常包含多个战术。
架构设计
在确定架构的需求之后,架构设计师需要将这些需求转化为具体的架构设计,也就是架构设计和架构文档化。架构设计的输入是一系列的架构驱动 Architectural Drivers
。它们通常包括:
- 功能性驱动
Functional Drivers
:系统需要实现的功能性需求。 - 质量属性驱动
Quality Attribute Drivers
:系统需要满足的非功能性需求。 - 约束
Constraints
:在架构设计过程中一定要遵守的限制条件或规则。 - 系统类型
Type of System
:系统的类型或领域,例如:绿地系统Greenfield
(全新开发)、棕地系统Brownfield
(现有系统的改造或扩展)等。 - 设计目标
Design Purpose
:进行架构设计的原因,例如:为了准备即将对系统进行扩增。 - 架构考量
Architectural Conserns
:架构设计中需要特别关注的方面,例如:性能、安全性、可维护性等。不同的考量细粒度不同。
在设计的过程中,我们有一系列架构设计理念 Concepts
。首先是架构设计的基本策略。这些策略相对较为通用,也比架构模式更加抽象。包括:
- 抽象
Abstraction
:将系统分解为更高层次的抽象,隐藏细节,关注整体结构和关系。 - 分解
Decomposition
:将系统分解为更小的模块或组件,便于管理和维护。 - 分治
Divide and Conquer
:将复杂问题分解为更小、更易处理的子问题,逐步解决。 - 生成并测试
Generate and Test
:通过生成多个设计方案并进行测试,选择最优方案。 - 迭代
Iteration
:通过反复的设计、实现和测试,逐步完善系统架构。 - 重用
Reuse
:利用已有的设计、组件或模式,减少重复工作,提高效率。
除了这些基本的策略之外,我们还可以使用一些架构模式,甚至一些已存在的架构来作为设计理念指导设计。有时,我们还可以直接使用一些现成的组件或框架来避免重复造轮子。有了这些工具的准备,下面我们详细介绍架构设计的过程。
属性驱动设计 Attribute-Driven Design, ADD
由于架构设计十分难掌握,应当用一种系统的方式来进行架构设计,因此诞生了诸多架构设计方法。其中,属性驱动设计(ADD 3.0
是最新的版本,它采用迭代式的设计方式,十分稳定且应用广泛,并且对 DevOps
和云原生等现代软件开发有很好的适应性。

属性驱动设计方法
属性驱动设计主要由如下几个部分组成:
- 检查输入
Review Inputs
:即检查前文的架构驱动,对它们排序,识别主要的功能性需求和质量属性场景,并保证它们和它们的优先级是正确的。 - 通过选择驱动建立迭代
Establish Iteration by Selecting Drivers
:通常每个迭代过程只专注于一个特定的目标,而这一目标一般只包括对某些架构驱动的设计。因此,需要建立合适大小的迭代目标,并且选择合适的架构驱动(一组相似或相关的架构驱动,或者仅仅是某一个架构驱动)作为迭代的输入。 - 选择一个或多个系统元素来精化
Select One or More Elements of the System to Refine
:精化可以通过将元素分解为更细化的元素,也可以通过将一些元素组合为更粗粒度的元素,还可以对之前确定的元素进行更详细的设计。对于绿地系统的早起开发,一开始可以选择整个系统作为一个元素进行分解;对于棕地系统或者绿地系统的后期开发,可以选择本次迭代中最高优先级的元素进行精化。 - 选择一个或多个满足所选架构驱动的设计理念
Choose One or More Design Concepts That Satisfy the Selected Drivers
:- 识别设计理念
Identify Design Concepts
:可以参考可选的架构,也可以根据过往经验和已有的架构模式来识别设计理念。 - 选择设计理念
Selection of Design Concepts
:可以使用Pros-Cons
表、SWOT
分析、原型法等方法来选择设计理念。
- 识别设计理念
- 实例化架构元素、分配职责并定义接口
Instantiate Architectural Elements, Allocate Responsibilities, and Define Interfaces
:将抽象的设计转换为具体的架构元素。 - 起草视图并记录设计决策
Sketch Views and Record Design Decisions
:这是架构最初的文档,应当记录下为各个元素分配的职责和相应的设计决策。可以使用形式化、半形式化等方式进行记录 - 对当前设计执行分析并检查迭代目标与成果
Perform Analysis of Current Design and Review Iteration Goal and Achievement of Design Purpose
:对本轮迭代的整体复盘。可以使用看板等方式管理未解决的、部分解决和已解决的问题。
当已经为所有的架构驱动都进行了迭代设计之后(或者已经到达了设计的时限),就可以结束迭代过程,并且将所有的架构设计文档化。
架构设计文档化
TODO soon