第9章 · 趣味学习

软件架构设计:系统的"布局谋篇"

推荐系统上线后,饿了么么的业务线越来越多——外卖、跑腿、超市、药品、鲜花...系统越来越庞大。小明发现每加一个业务线就得改一堆模块,改了东墙塌西墙。他意识到:单个模块设计再好也没用,如果整体架构没有"布局谋篇"

就像写作文——字词句再优美,段落组织不好,整篇文章就是一盘散沙。软件架构设计就是软件系统的"布局谋篇",是架构设计师的真正主场。

🏛️ 9.1 软件架构概述:从子程序到中间件

软件抽象的发展史:

  • 1960s 子程序年代:程序间调用为连接关系
  • 1970s 模块化年代:数据流分析、E-R图、信息隐藏,抽象到模块级
  • 1980s 面向对象年代:继承增加了新元素连接关系
  • 1990s 框架年代:可互换的黑箱对象相互嵌入
  • 当前 中间件和IT架构:可购买可复用元素构建系统

软件架构定义(采用定义1):系统的一个(或多个)结构,由软件元素、元素的外部可见属性、元素之间的关系组成。关键理解:架构是抽象,强调"外部可见"属性,内部实现细节不属于架构。

架构重要性四方面:① 项目干系人交流的平台(共同语言)② 早期设计决策(约束实现、影响质量属性、预测质量、维护依据、原型开发、成本进度估计)③ 较高层面上实现复用(产品线共享架构)④ 对开发的指导与规范。

架构5种模型:结构模型(最直观,构件+连接件)、框架模型(侧重整体结构)、动态模型(系统大颗粒行为)、过程模型(构造步骤)、功能模型(功能构件分层)。最常用=结构模型和动态模型。

"4+1"视图模型(Kruchten 1995):

视图 关注 谁看
逻辑视图功能需求,对象模型最终用户
开发视图模块组织管理程序员
进程视图运行特性,并发/分布/容错系统集成人员
物理视图软件到硬件映射系统工程师
场景(+) 重要活动抽象,串联四视图所有人

逻辑+开发视图=静态结构,进程+物理视图=动态结构。

⚖️ 9.2 软件质量属性:架构师关注的焦点

架构设计师重点关注质量属性而非功能属性——因为不同结构可实现同样功能,功能性独立于结构。架构设计师面临的是对结构选择的决策

运行期质量属性:性能、安全性、易用性、可伸缩性、互操作性、可靠性、持续可用性、鲁棒性(健壮性/容错性)。

开发期质量属性:易理解性、可扩展性(灵活性)、可重用性、可测试性、可维护性、可移植性。

6个质量属性+战术(战术=实现质量属性的基本设计决策,战术集合=架构策略):

可用性——阻止错误发展成故障。战术三类:

  • 🔍 错误检测:命令/响应、心跳(计时器)、异常
  • 🔄 错误恢复:表决、主动冗余(热备份)、被动冗余(暖重启)、备件、状态再同步、检查点/回滚
  • 🛡️ 错误预防:从服务中删除、事务、进程监视器

可修改性——局部化修改、防止连锁反应、推迟绑定时间。战术:维持语义一致性、预期变更、泛化、限制选择;信息隐藏、维持现有接口、限制通信路径、仲裁者使用;运行时注册、配置文件、多态、构件更换。

性能——影响响应时间=资源消耗+闭锁时间。战术:减少资源需求(提高效率/减少计算/控制事件率/限制执行时间)、资源管理(引入并发/多副本/增加资源)、资源仲裁(FIFO/固定优先级/动态优先级/静态调度)。

安全性——抵抗攻击(身份验证/授权/加密/完整性/限制暴露/限制访问)、检测攻击(入侵检测)、从攻击中恢复(恢复+识别攻击者)。

可测试性——输入/输出(记录回放/接口与实现分离/优化访问线路) + 内部监控。

易用性——运行时(任务模型/用户模型/系统模型) + 设计时(分离用户接口) + 支持用户主动操作(取消/撤销/聚合/多视图)。

💡 秒懂技巧:质量属性间常常鱼和熊掌——表9-1中"+"代表行促进列,"-"代表副作用。性能对很多属性有副作用(第一列多是"-"),很多属性对性能也有副作用(第一行也多是"-")。架构师的工作就是在矛盾间权衡

🎨 9.3 软件架构风格:5大家族

架构风格=描述某一特定应用领域中系统组织方式的惯用模式。定义词汇表(构件/连接件类型)+约束(如何组合)。Garlan和Shaw分类5大家族:

风格家族 子风格 核心思想
📊 数据流批处理序列、管道/过滤器数据像流水线一样流动加工
📞 调用/返回主程序/子程序、OO、层次结构分而治之,调用返回
🔗 独立构件进程通信、事件系统构件独立不直接通信
🖥️ 虚拟机解释器、规则为中心构建运行环境解析自定义语言
🏪 仓库数据库、超文本、黑板中央数据结构+独立构件操作

管道/过滤器:每个构件读输入→内部处理→产生输出,构件=过滤器,连接=管道。UNIX shell和编译器是经典例子。优点:隐蔽性好、支持重用、可并行。缺点:不适合交互应用、解析合成数据增加复杂性。批处理vs管道过滤器:批处理数据整体传递、高潜伏性、无交互;管道过滤器递增、延迟小、可交互。

层次结构:每层为上层服务并作为下层客户。优点:抽象递增设计、功能改变最多影响相邻两层、支持重用。缺点:不是所有系统都能分层、找到合适层次抽象难。最广应用=分层通信协议。

事件系统(隐式调用):构件不直接调用,而是触发/广播事件,其他构件在事件中注册。触发者不知道谁响应。优点:强大重用支持、改进方便。缺点:放弃计算控制、数据交换问题、正确性推理困难。

黑板系统三部分:知识源(不直接通信,通过黑板交互)、黑板数据结构(按层次组织解决问题数据)、控制(黑板状态驱动)。传统应用=信号处理(语音/模式识别)。

🏗️ 9.4 层次架构:C/S、B/S、MVC、MVP

二层C/S缺点:单一服务器难以扩展到广域网、服务器负荷太重、数据安全性不好(客户端可直接访问数据库)。

三层C/S:表示层(用户接口) + 功能层(业务处理逻辑) + 数据层(DBMS)。关键=表示层和功能层分离成独立程序。功能层和数据层放不同服务器=灵活性最高,系统越大优势越显著。

B/S(浏览器/服务器):三层C/S的实现方式,浏览器→Web服务器→数据库服务器。零客户端——只需浏览器。优势:维护全在服务端、异种机异种网开放性。不足:缺乏动态页面支持、响应速度低于C/S、数据动态交互性差、不利于OLTP。

MVC(1979年Reenskau提出):

  • 📊 Model:应用状态和业务功能的封装(数据+行为),接受Controller请求完成业务处理,状态改变时通知View
  • 👁️ View:可视化界面呈现+捕捉用户交互操作
  • 🎮 Controller:完成UI逻辑。View捕获操作→转发Controller→需要业务调用Controller调Model→控制原View或创建新View响应

MVC解决的核心问题=关注点分离——业务逻辑、UI处理逻辑、界面呈现三者分离。避免自治视图的问题:业务逻辑丧失重用、不同稳定性元素混在一起、UI组件难测试。

MVP(Model-View-Presenter):从MVC演变,关键区别=View不直接使用Model。所有交互在Presenter内部。Presenter依赖抽象化View(IView接口),使UI处理逻辑易于测试。优点:模型视图完全分离、一个Presenter可用于多视图、脱离UI测试逻辑。缺点:View和Presenter交互频繁,过度渲染会与特定视图联系过紧。

💡 秒懂技巧:MVC中View可以直接从Model读数据("混乱的交流");MVP中View和Model完全隔离,一切经过Presenter。MVP=MCV的"更严格版"——连View和Model说话都不允许了,必须通过Presenter传话。

🌐 9.5 面向服务的架构(SOA)与微服务

SOA核心:所有功能定义为独立的服务,通过交互协调完成业务逻辑,服务间松散耦合

5个设计原则:明确定义的接口、自包含和模块化、粗粒度(消息交互而非RPC,频度低)、松耦合、互操作性/兼容/策略声明。

关键技术(以XML为基础):

  • 📋 UDDI:服务发布、查找、定位。三部分:数据模型(XML Schema)、API(基于SOAP)、注册服务
  • 📄 WSDL:服务描述语言。服务接口定义(抽象可重用) + 服务实现定义(端口/部署细节)
  • ✉️ SOAP:消息传输规范。用XML格式化消息,HTTP承载。四部分:封装(Envelope)、编码规则、RPC表示、绑定。消息三部分:Envelope(必须)、Header(可选)、Body(必须)
  • 🔄 REST:只使用HTTP和XML。所有事物抽象为资源,每资源唯一标识,通用接口操作,操作不改变资源标识,无状态。支持POST/GET/PUT/DELETE

SOA三种实现方法

  • 🔌 Web Service:三角色(提供者/请求者/注册中心),三操作(发布/查找/绑定)。6层架构:传输层→通信协议层→描述层→服务层→业务流程层→服务注册层
  • 📖 服务注册表:设计时使用,服务注册/服务位置/服务绑定
  • 🚌 ESB(企业服务总线):标准化通信基础结构。消除服务提供者与请求者间直接连接,进一步解耦。功能:异构环境支持、非侵入式服务接口、缓冲器、服务代理/协议转换、消息转换路由、安全认证

微服务——很小的服务,属于SOA的一种。像活字印刷术——每个服务独立组件,通过编排组合使用。核心特点:小且专注做一件事、轻量级通信(RESTful)、松耦合、独立部署

微服务7大优势:技术异构性(每服务可选技术)、弹性(局部故障不整体崩)、扩展(针对单服务)、简化部署、与组织结构匹配、可组合性、可替代性优化。

微服务6大挑战:分布式复杂度、运维成本、部署自动化、DevOps与组织结构、服务间依赖测试、服务间依赖管理。

微服务 vs SOA主要差异:微服务更细粒度、更独立部署、更强调去中心化数据管理、更轻量通信。

📐 9.6 架构设计:ADD方法

属性驱动设计法(ADD)——将分解建立在质量属性之上。输入=功能需求(用例)+限制条件+质量需求(质量场景)。

ADD步骤:① 选择要分解的模块 → ② 选择架构驱动因素(优先级最高的需求) → ③ 选择满足驱动因素的架构模式(基于战术) → ④ 实例化模块并分配功能(多视图表示) → ⑤ 定义子模块接口 → ⑥ 验证用例和质量场景 → ⑦ 对需进一步分解的模块重复。

演变交付生命周期模型两个循环:① 迭代开发架构 ② 在架构基础上迭代开发最终版本。架构由架构驱动因素(少数关键的高优先级业务目标质量需求)塑造。开发骨架系统是第二个循环的第一步。

📝 9.7 软件架构文档化

架构文档使用者=项目关系人。7条编档规则:从读者角度编写、避免不必要重复、避免歧义、使用标准结构、记录基本原理、保持更新但不过频、针对适宜性评审。

视图编档7部分:视图概述、元素目录(元素/关系/接口/行为)、上下文图、可变性指南、架构背景、术语表、其他信息。

架构重构(反向工程之一):系统已存在但架构未知(文档缺失/失效)。四活动迭代:信息提取→数据库构造→视图融合→重构(可视化和交互+模式定义和识别→生成架构文档)。

🔍 9.8 软件架构评估

三种评估方法

  • 📋 基于调查问卷/检查表:利用经验知识,缺点=依赖主观推断
  • 🎭 基于场景:SEI提出,分析架构对场景的支持程度。ATAM和SAAM使用
  • 📊 基于度量:建立质量属性-度量映射→获取度量信息→推导质量属性。更客观量化

ATAM(架构权衡分析法)——9步:

  1. ATAM方法表述
  2. 商业动机表述
  3. 架构表述
  4. 架构方法分类
  5. 生成质量属性效用树(根→质量属性→属性求精→场景(叶),修剪保留≤50个,按重要度和难易度各H/M/L排序)
  6. 分析架构方法(探查实现场景的架构方法,找风险/无风险/敏感点/权衡点)
  7. 集体讨论并确定场景优先级(投票)
  8. 分析架构方法(类似第6步,针对第7步的高优先级场景)
  9. 结果表述

ATAM产生结果:简洁架构表述、业务目标、场景集合、架构决策到质量需求映射、敏感点和权衡点、有/无风险决策、风险主题。

CBAM(成本效益分析法)——在ATAM上构建,从经济角度建模。8步:整理场景→求精(最坏/当前/期望/最好)→确定优先级(投票)→分配效用→策略-场景-响应级别对应→内插法确定期望效用→计算总收益→按ROI排序选策略。

🎮 实战场景:饿了么么评估推荐系统架构用ATAM——发现"缓存数据库"这个决策对性能有正面影响(+)但对数据一致性有负面影响(-),是权衡点。通过效用树发现最重要的场景是"高峰期推荐响应<200ms"(重要度H,难易度M),架构师重点分析这个场景的实现方法。

🧩 9.9 构件及复用

三大商用构件标准:

标准 组织 核心
CORBAOMG3层:ORB(软总线)→公共服务→公共设施。CCM构件模型
J2EESUN支持RMI+IIOP。Servlet/JSP/EJB。EJB=会话Bean+实体Bean
DNA 2000MicrosoftCOM/DCOM/COM+。ASP+COM+Cluster。负载均衡/内存数据库/对象池

基于复用开发的组织结构:三类职能部门——构件系统开发部(开发可复用资产)、应用系统项目开发部(复用资产)、支持部门(确认/编目/分发/反馈)。上有复用经理协调,平衡长期目标与近期项目压力。

🏭 9.10 产品线与系统演化

软件产品线=一组共享公共可管理特性集的软件密集型系统,用公共核心资产集成开发。核心资产库包括架构+可剪裁元素+设计方案+文档+测试计划+历史记录等。

可复用资产:需求、架构设计、元素、建模与分析、测试、项目规划、过程/方法/工具、人员、样本系统、缺陷消除。

产品线架构3点特别之处:① 必须考虑一系列明确许可的变化 ② 一定要文档化 ③ 必须提供"产品创建者指南"描述实例化过程。

两种开发模型前瞻性产品线(自上而下,基于战略决策预先设计) vs 反应性模型(自下而上,根据已证明为共有的元素构建)。

DSSA(特定领域软件架构)——开发产品线的方法。必备特征:严格定义的问题域/解决域、普遍性、合适抽象程度、固定可复用元素。垂直域(特定系统族通用架构) vs 水平域(多系统族共有功能)。活动阶段:领域分析(获得领域模型)→领域设计(获得DSSA)→领域实现(开发组织可复用信息)。中等复杂度项目约50-100个类。

架构演化7步:需求变动归类→制订演化计划→修改/增加/删除构件→更新构件相互作用→构件组装与测试→技术评审→产生演化后架构。

👁️ 9.11 软件架构视图

视图分三类:

  • 📦 模块视图类型:主要模块实现单元。4种风格——分解(分配责任)、使用(模块依赖)、分层(虚拟机"允许使用"关系)、泛化(继承关系)
  • ⚙️ 构件和连接件(C&C)视图类型:运行时元素。6种风格——管道和过滤器、共享数据、发布-订阅、客户机-服务器、对等连接、通信-进程
  • 🗺️ 分配视图类型:软件到环境的映射。3种风格——部署(硬件映射)、实现(文件系统映射)、工作任务(团队映射)

各视图间映射关系:模块视图→C&C视图(模块实现单元映射到运行时构件,但关系复杂——同一代码模块可由多个C&C元素执行,单一构件可执行多模块代码);分配视图=辅助性,将其他视图映射到环境。

💡 秒懂技巧:三种视图像看医院——模块视图=科室划分(内科/外科/儿科,各管什么),C&C视图=运行时谁和谁通信(医生→护士→药房→检验科),分配视图=谁在哪栋楼(心血管科在3楼东翼、检验科在1楼)。完整架构=三种视图缺一不可
🎬 故事结局:小明终于完成了架构设计全套修炼——定义(元素-结构-架构)、5种模型(4+1视图)、6个质量属性(可用性/可修改性/性能/安全性/可测试性/易用性)及战术、5大架构风格家族、层次架构(C/S/B/S/MVC/MVP)、SOA与微服务、ADD设计法、文档化、架构评估(ATAM/CBAM)、构件复用(CORBA/J2EE/DNA)、产品线与DSSA、三种视图类型。

饿了么么最终选了微服务+分层架构:每个业务线(外卖/跑腿/超市)是独立微服务,通过RESTful API通信。表示层用B/S,业务逻辑层用微服务,数据层用分库分表。用ATAM评估发现缓存策略是关键权衡点,用4+1视图编档架构。

老王感慨:"架构设计就是选和妥协——不是选最好的,是选最合适的。"但小明知道:架构设计还要落地到代码层面——第10章的设计模式,是架构思想的微观体现。