https://www.ibm.com/developerworks/cn/rational/1312_wanggb_arch/index.html
什么是软件架构
如果期望有一个权威统一的标准定义,那答案是没有,目前存在多种软件架构的定义,可以说百花齐放,百家争鸣。其中 IEEE1471-2000 的定义是这样的:系统的架构是系统组件的基本组织形式,它们之间的关系以及和环境之间的关系,以及指导其设计和演化的原则。该定义中的系统组件可以理解为架构元素,根据涉及到的系统范围和层次,架构元素可以是子系统、模块、类等等。从架构设计的动态角度出发,我们可以这样来定义软件架构:通过一系列架构决策,将系统分解为一些架构元素,并定义这些元素之间的接口和交互关系、集成机制。架构决策就是在架构设计过程中做出的一系列全局的决定和权衡取舍,例如将系统拆分成几个子系统、子系统的职责是什么、子系统之间的接口是什么、采用什么通讯方式和集成机制、采用的开发语言和技术框架等。
架构形而上的本质
除了软件架构,众所周知,在建筑行业中也存在架构,而且建筑架构比软件架构历史要悠久得多;在公司中存在组织架构。这就引发了我们的思考,对这些不同的架构,它们的本质是什么?
从抽象的形而上的角度来看,架构是架构作用力的平衡,如图 1。
图 1 中左侧一个最初混沌的系统在架构作用力的相互作用下达到足够程度的平衡。右侧的椭圆蕴含的意思是架构应该是有弹性的,里面的多个小圆表示在力的作用下分解产生的架构元素。
图 1.形而上的架构
什么是架构作用力
对一个建筑工程项目,会涉及到多方涉众,如建设单位、勘察单位、设计单位、施工单位、工程监理单位、使用单位等等,他们对工程项目存在各种需求;同样在一个软件系统中也会有多种涉众,例如系统用户、投资人、需求分析师、架构师、开发人员、测试人员、客服人员、运维人员等等。这些涉众在架构层面有各自的架构需求,例如系统用户除了要求满足他们的功能需求外,还有对系统的易用性、性能、可用性等非功能性存在要求;开发设计人员希望系统架构清晰,便于理解,关注代码重用性、扩展性、可维护性。架构师更关注于系统可伸缩性、可用性、性能等非功能需求。这些需求必须在架构层面加以考虑,使这些涉众的需求在架构层面得到足够的满足。
从抽象的层次来看,涉众在架构层面的上述各种需求就是架构作用力,它们直接影响和驱动架构设计。大师 Grady Booch 在他的<<The architecture of Web applications>>一文中就已经关注软件中的作用力(Forces in software)。在架构中除了存在这些外力外,在架构中也存在内力,就是架构元素之间的相互作用力,这里不展开讨论。
一个架构要满足所有的需求通常是不可能的,因为不同的涉众,他们之间的需求可能是相互冲突的,也就是作用力的方向是相反的。作用力的大小主要体现在需求的优先级上。
架构设计过程中,架构师运用自己的经验,结合当前的技术,从全局考虑需求和约束,进行折中和取舍,平衡这些涉众的作用力。
在 IEEE1471-2000 规范中,将涉众在架构层面的各种需求称为关注点,从这个概念来说架构分解就是关注点分离。
架构分解
当我们看到一个复杂系统的架构视图时,可能会思考这些视图中的架构元素是怎么来的。如何从混沌的铁板一块的整体架构演变成一个可伸缩高性能的分布式系统?
分而治之是一种处理复杂问题的通用方法,在软件架构中,它也是一种很重要的手段,例如多层架构、OSI 七层模型都体现了分而治之思想。在架构设计过程中,通过将关注点分离对架构进行多层次分解,将系统层层分解为多个架构元素,进而识别架构元素。
可以说架构中的很多元素都是在架构分解的过程中识别产生的。
架构分解的作用
万事开头难,架构分解是架构设计过程中非常关键的一步。除了识别架构元素,对大规模的软件系统,分解还是解决非功能需求的重要手段。eBay 为了解决可伸缩性、可用性、可管理性等问题,在架构的多个层面进行了分解:
- 在应用层面,按照功能或 SOA 服务进行分解,将系统垂直拆分为多个应用池(应用池中的服务是无状态的)。每个应用池中有多个应用(水平拆分),可以独立灵活地进行伸缩。见图 2 所示。
- 在数据层面,对数据进行垂直拆分(分库)和水平拆分(数据分片 DB Sharding);将分布式事务拆分成多个本地事务独自提交,避免分布式事务。见图 3 所示。
图 2.应用的垂直和水平拆分
图 3.数据的垂直和水平拆分
架构分解的原则
德国哲学家、数学家莱布尼兹一针见血地指出:“不讲分解技巧,分而治之就不大有用。无经验者对问题分解不当,反而会增加困难“。为了正确的进行分解,需要遵循一些分解原则:
- 低耦合、高内聚:莱布尼兹指出:“分解的主要难点在于怎么分。分解策略之一是按容易求解的方式来分,之二是在弱耦合处下手,切断联系”。在弱耦合处下手,切断联系。太精辟了!高内聚、低耦合也是软件设计的基本原则,软件设计中的很多设计原则其实都可以认为它的派生或具体化,如单一职责原则、依赖倒置原则、模块化封装原则,这些原则在架构分解中也是适用的。
- 层次性:分解通常是先业务后技术,循序渐进,先逻辑后物理,从上到下逐级进行分解展开:系统->子系统->模块->组件->类。
- 正交原则:和物理学中的正交分解类似,架构分解出的架构元素应是相互独立的,在职责上没有重叠。
- 抽象原则:架构元素识别,在较大程度上是架构师抽象思维的结果,架构师应该具备在抽象概念层面进行架构构思和架构分解的能力。
- 稳定性原则:将稳定部分和易变部分分解为不同的架构元素,稳定部分不应依赖易变部分。根据稳定性原则,将通用部分和专用部分分解为不同的元素;将动态部分和静态部分分解为不同的元素;将机制和策略分离为不同的元素;将应用和服务分离。
- 复用性原则:就是对知识的重用.重用类似系统已有的架构设计、设计经验、成熟的架构模式或参考模型、设计模式、领域模型、架构思想等,因为它们已经在不同的层次上分解识别出了许多架构元素,或者指出了一些分解方向,对我们的架构分解具有借鉴和指导作用。例如 IBM SOA 解决方案参考模型对 SOA 服务化具有重要的指导意义,我们可以参照它对系统进行初步的架构分解。
图 4 IBM SOA 解决方案参考模型
架构分解的过程模型
对复杂的系统,特别是前人没有做过的新系统,通常难以一下子设计出合适的架构。在架构设计的初期,通常都要经历一个不断探索的阶段。
在架构设计过程中,架构分解是必不可少的的关键步骤。如何进行架构分解?从哪里入手开始进行分解?我们需要有一个架构分解的过程模型来指导分解过程,启发和探索架构分解的维度和线索,提高架构分解的效率。
架构分解过程模型如图 3 所示,是一个迭代的模型。通过这个迭代的分解,从无到有、从粗到细、从模糊到清晰,一步步精(细)化、丰富架构。迭代的过程也是一个否定之否定的过程,随着分解的逐步推进或系统的架构演化,后面的分解除了会识别出新的架构元素,也可能会对先前识别出的架构作出调整。
图 5.架构分解过程模型
依次在 4 个域中进行架构分解,基本顺序是先业务后技术,通过多维度多层次的分解将关注点分离。
- 业务域分解:先从业务域进行分解,狭义的业务域具有商业的概念,从这个概念来看,有的系统没有业务域,但如果宽泛一点来看,业务域就是问题域(问题空间),问题域总是存在的。
首先是从系统需求入手,寻找业务域中的分解维度,将架构从业务域层面进行大尺度(大粒度)的分解。在业务域中进行分解,通常采用的分解维度是根据业务主题,将系统分解为多个子系统,每个子系统聚焦于一个独立的业务主题,子系统间具有清晰的边界。例如对某电商系统,我们可以根据业务主题维度进行架构分解,初步划分出:会员子系统、交易(订单)子系统、产品子系统、搜索子系统、物流子系统、支付子系统等。
领域驱动设计(DDD)中的边界上下文(context boundary)是一种根据业务相关性进行大尺度分解的方法,它和基于业务主题的分解是类似的。
图 6 基于业务主题的分解
对业务域分解不应只局限于基于业务主题的分解,根据具体情况,还可能有其他的分解维度。一个通用的发现分解维度的方法是试着从领域模型和需求分析文档中寻找名词和形容词,将文档中的核心概念(名词和形容词)作为分解的候选分解维度或分解线索。
在业务域的分解中,我们要和业务(行业)专家密切交流,多研究业务架构、适当考虑企业战略,这样可在一定程度上保证架构分解的合理性。
- 业务功能域分解:通过对业务流程和用例进行分析,根据功能职责,进行垂直和水平分解,识别出业务功能或业务服务,将它们归类到子系统中相应模块中去。
对业务域功能域分解,一个通用的方法是试着通过动词来将子系统拆分为多个服务;另一个是根据资源类型(名词)来将系统拆分为服务,每个服务负责实现对应资源上的一组操作。例如根据资源类型(会员、商品等),对电商系统,可分解识别出会员查询服务和会员管理模块、商品服务等。
这两步的架构分解都是纯业务上的,不涉及技术,但要这两步分解出的架构元素要映射到技术域中去实现,或用来帮助架构师推导出技术域的对应架构元素。
- 技术域分解:是从技术角度对系统和模块进行分解。在该阶段,通常会选取关键的需求(包括功能需求和非功能性需求)和已分解出的模块或子系统,结合当前的 IT 技术(技术框架、架构模式、参考架构、中间件、业务平台)和架构思想、架构经验、开发人员的技能以及系统的上下文环境等,进一步进行架构分解。
例如很多系统采用 MVC、多层架构模式、SOA、事件驱动架构等技术或思想进行技术域中的架构分解。对电商交易子系统的订单流程,考虑到要需要灵活支撑多种交易流程和解耦流程层与业务逻辑层的需要,按照 IBM SOA 的参考架构(图 4)可以分拆出流程层、服务层等,在流程层中采用流程引擎技术。
在技术域分解中,对功能需求,横切(分层)竖割是一种常用的分解手法。对非功能需求,可将性能、伸缩性、可用性等作为维度对系统进行分解,在非功能需求分解战术中将专门说明这些维度的分解技术(称为战术)。
在业务域和业务功能域中分解出来的架构元素,考虑到技术实现,在映射到技术域时,可能要对架构元素进行修改或作一些微调,业务域和业务功能域中分解出的架构元素通常会进一步分解为技术域中的多个架构元素。例如在业务功能域分解出的会员查询服务模块,在技术域中分解时,考虑到有多个不同类型的调用方会使用该服务,并且这些调用方需要的查询接口差异较大,调用频率等也不一样,那我们可能会对每个调用方拆分出独立的查询接口模块,这样也避免了胖接口,相关的 DAO 类也是在技术域中新产生的。
在从模块分解到类的过程中(假设有些模块要分解到类级别),需要进行静态和动态的分析以便识别出类元素。首先是根据业务域需求,建立领域模型,识别出领域(概念)类,再将领域类映射到技术域中的设计类(实体类),也就是领域类可帮助我们识别推导出部分设计类。再基于具体用例,进行鲁棒分析和序列图设计,考虑技术上的实现和采用的技术架构,例如采用一些技术框架和类库或工具类,如 ibatis 框架,就会识别出另一些设计类(边界类、控制类)或引入一些新的设计类,这些引入的类可能直接来自框架或类库,或是继承框架中的类(成为框架类的子类)。通常设计过程不会就此结束,考虑到开发维护和设计质量等因素,我们会运用一些设计原则(如高内聚低耦合、单一职责、信息专家等)、设计模式、重构模式等对设计进行进一步的优化,这时又会产生识别出一些设计类,通常在这一步会对我们的设计进行一些修改和调整,例如采用工厂方法设计模式,会引入工厂方法类,我们的序列图就要修改。这样我们最终完成了一个完整的设计。因为要考虑技术域,所以对整个分析设计来说,它是源于业务但要高于业务。
在技术域的分解中,对公共的技术需求应全盘考虑,抽象出底层的公共技术基础设施,例如定时任务在许多子系统中都存在,此时可能会规划一个定时任务框架和定时任务执行系统。也可能会采用一些成熟的框架和中间件技术,如消息中间件、ESB 等。
技术域的分解通常是比较复杂的,这一方面来源于问题域的本质复杂性,特别是各种非功能性需求的复杂性,需要架构师掌握应对这些需求的常见模式。另一方面也是由于 IT 新技术的日新月异,要求架构师对技术敏感,与时俱进。
要注意的是,当在技术域分解中碰到困难时,可以再回到业务域中去寻找答案和线索。例如为了解决某计费系统中的性能和可伸缩性问题,可在业务域中寻找名词和形容词,发现可以根据付费类型(预付费和后付费)将系统初步分拆为预付费系统和后付费系统。对网络通讯系统,可以根据请求消息中的某些属性对消息处理系统进行拆分或根据客户端类型等进行拆分。
- 涉众域分解:全面考虑各类涉众在架构层面的关键需求,特别是非功能需求,例如性能需求、可伸缩性需求等,进一步对系统进行分解。
涉众域分解包括了前面说的业务域分解、业务功能域分解、技术域分解,通常涉众域分解和它们有部分是重叠的,例如在技术域和涉众域中都有性能方面的架构分解。涉众域分解保证我们的分解是完备的,没有遗漏。在涉众域的分解过程中,很可能会产生新的子系统,例如考虑用户的性能方面的需求,可能会分解出分布式数据缓存子系统(也可能在技术域分解中就已经考虑到了)。
涉众域分解的一个可能的维度是根据涉众类型,基于他们的关注点来进行架构分解,试着为某类涉众划分出对应的子系统或模块(架构元素),例如考虑到前台涉众和后台涉众的用户类型、关注点差别较大等因素,可能会将系统划分为前台系统和后台系统两大类。对第三方涉众(外部系统),考虑到接入认证授权、安全性和互操作性,可能会分解出网关子系统。
为何首先从业务域开始分解,其实很好理解,我们开发一个系统肯定是为了解决业务问题,是为业务服务的,不可能脱离业务去设计一个空洞的无目标的系统和架构。
架构分解维度
从上面的描述可以看出,架构分解就是从多个维度多层次对系统进行分解,识别出架构元素,逐步精化、丰富系统架构的过程。图 4 形象地表示了架构分解的多维度立体拆分。
图 7.多维度分解
这 4 个域中除了分解过程模型一节中提及的一些分解维度,根据具体的系统,还可发掘出许多分解维度,如时间维度、物理空间维度、优先级维度、职责角色维度(不同的角色)、客户端维度、调用方维度(不同的调用方)、请求类型维度、数据维度、数据处理维度(OLAP、OLTP)等。
有几个要指出的问题是:
- 分层和分解的不同,分层只是分解的手段之一,是一种有层次的分解,还有很多分解手段不属于分层,例如基于业务主题的分解和各种数据分解。
- 横切竖割式的二维分解是一种分解形式,对复杂系统,可能要从 4~5 个或更多维度对系统进行较大尺度的分解。
- 授人以鱼,不如授人以渔。本文列出的一些分解维度只是为了帮助阐释多维度多层次分解的方法,重点是分解方法和一些发现分解维度的策略,不是列出的分解维度,不要本末倒置。在运用多维度分解方法时,不要局限于这些维度,也有可能这些分解维度不适合你的产品或项目,应根据实际情况,去发现合适的分解维度。
- 在架构设计中,除了分解,还有集(合)成,在分解时要有整体思维,在分解的过程中要考虑到和其他架构元素的集成,保证分解出的架构元素最终能集成在一起。
- 软件架构中科学与艺术并存,注定了架构设计(包括架构分解)没有银弹,但我们可以不断努力探索架构设计中的科学成分,欣赏伟大架构师的设计艺术。
非功能需求的分解战术
对非功能性分解维度,在具体的分解过程中,可以借助一些成熟的模式来实现架构元素的识别,在<<软件构架实践>>中,将在架构层面应对非功能需求特性的架构模式或架构策略称为战术,例如对可用性,常用的战术包括:冗余、错误检测。
在<< The Art of Scalability >>中,对可伸缩性,提出了一个伸缩立方模型(Scale Cube),如下图 5。
图 8.伸缩立方模型
这是一个三维的立方模型,表示可在三个维度上实现可伸缩性:
- X 轴表示水平复制(克隆),就是在负载均衡器后面部署多个相同的应用来进行水平伸缩;
- Y 轴表示功能分解,按不同的功能对应用进行分解。也可用于数据,就是分库,不同类型的数据在不同的库中。
- Z 轴表示数据分片(sharding),每个数据分片有单独的应用负责访问。也可表示应用分片,每个应用只负责提供部分数据服务,多个应用合起来提供完整的服务。
这三个维度可以结合在一起使用,也可单独使用。
可以看出,前面提到的 eBay 可伸缩性实践应用了该模型。
部分非功能需求的常用战术如下表 1:
表 1. 非功能需求战术
非功能需求类型 | 战术 |
---|---|
可用性 |
|
性能 |
|
可伸缩性 |
|
分解维度与战术的关系
战术除了应用在非功能需求上,在架构层面应对功能性需求的架构模式、架构策略和其他技术手段也可称为战术。例如分层模式、AOP(面向切面编程)技术等都是战术。
分解维度和战术的关系,就是战略和战术的关系,分解维度指明分解的位置和目标所在,战术是实现该分解的手段。例如在森林里伐木,分解维度指出砍哪颗树,战术指出是用斧头砍还是锯子锯。没有战略,就不知道目标和方向,成为无头苍蝇;没有战术,战略就成空想,不能落地实现。对软件系统中的横切关注点,在 AOP 技术出现之前,知道要进行分离,但一直没发现实现分离的完美战术,直到 AOP 技术出现后,才较为完美的实现了横切关注点分离,可见战术和机制也很重要。
对简单的系统或有经验的类似系统,也可直接应用成熟的战术,不需在架构分解上过多投入。
架构分解的粒度
多维度多层次分解到什么粒度才停止?这个没有统一的标准,通常要能进行并行开发,能指导后续的详细设计。需要根据具体的产品或项目来定,有的到模块级别就行,对关键的部分,可以到类级别。
架构分解的时机
架构分解的时机通常就是架构改造演化的时机。当架构出现腐化和臭味,已经难以满足关键涉众的关键需求,例如用户的响应速度越来越慢已经接近临界值,并且根据预见,响应速度还有可能继续较低;开发人员越来越难以维护,这个时候可以考虑进行架构演化,对架构进行改造。当然如果能提前预见系统的问题,经过慎重评估后,在问题发生之前,提前一段时间进行架构演化也是可以的。
要注意的问题是不要过度分解,过早分解,这样做除了增加成本,还可能带来风险。例如很多系统在建设初期,考虑到规模较小和快速上线,通常都是一个整体的系统,不会进行大的架构分解,以后随着需求和规模的逐渐增加,会逐步进行架构改造和架构分解。
架构视图和架构视点
在 IEEE1471-2000 规范中对视图和视点的定义如下:
- 视图:是从相关的一组关注点投射的整个系统的一个表示。为了捕获或表示系统架构的设计,架构设计师一般会创建一个或多个架构模型,可能用不同的工具。视图由这其中选择出来的一个或多个模型组成,选择这些模型是为了展示个一个或一组利益相关人,以便他们的关注点在设计系统架构的过程中获得了足够的关注。
- 视点:架构视点是用于构建和利用一个视图的约定规范,它是一个模式或模版,可以用来确定一个视图的目标、用户以及创建和分析的技术,从而建立一个独立的视图。
视点可理解为观察系统的一个角度,每个系统有多个视点,每个视点都有一个涉众;该类涉众从某个角度观察系统,所看到的系统的部分信息就是该视点对应的视图,系统有多个视图。
视图和视点通常用在架构文档中,作为描述架构的重要手段,在架构分解过程中,可用来作为发现架构分解维度的一种输入,帮助进行架构分解。
在 RUP 中定义了“4+1“视图,其中的开发视图就是开发人员从开发视点出发,用来为开发人员提供切实的指导。
除此之外,在企业架构 TOGAF 框架和 RM-ODP 也定义了视点和视图来建模和描述各种类型的架构。
总结
对复杂的软件系统,其架构设计是一项复杂的系统工程,架构分解作为架构设计中的关键步骤,在软件行业中还没有成熟的系统的架构分解方法论来指导架构分解。本文对此问题做了初步的探讨,提出了架构分解的过程模型和架构分解多维度模型,用来帮助架构师逐步进行架构分解,识别出其中的架构元素。对架构分解,关键在于找到合适的架构分解维度和分解战术。