一直以来,因为软件架构涉及范围广且内涵不断变化,开发人员不断尝试给它一个简洁的定义。Ralph Johnson 就将其定义为“重要的东西(不管那是什么)”。架构师的工做就是理解和权衡那些“重要的东西”(不管它们是什么)。前端
为了给出解决方案,架构师工做的第一步是理解业务需求,也即领域需求。这些需求是使用软件来解决问题的动机,但终究只是架构师在构建架构时须要考虑的因素之一。架构师还必须考虑其余不少因素,其中一些比较明确(好比清楚地写在性能服务水平协议里),还有一些则隐含在商业活动中不言自明(好比公司正着手并购重组,软件架构显然也要有变更)。因此对于软件架构师来讲,架构水平体现了他们在权衡业务需求和其余重要因素后找到最佳方案的能力。软件架构涵盖了全部这些架构因素,如图 1-1 所示。算法
图 1-1:架构的完整概念,包括需求及“各类特征”数据库
如图 1-1 所示,业务需求与其余架构关注点(由架构师定义)并存。这包括各类外部因素,这些因素能够改变构建软件系统的决策过程。表 1-1 列举了一些例子。编程
表1-1:部分特征列表后端
可访问性 | 可问责性 | 准确性 | 适应性 | 可管理性 |
可负担性 | 敏捷性 | 可审计性 | 自治性 | 可用性 |
兼容性 | 可组合性 | 可配置性 | 正确性 | 可信度 |
可定制性 | 可调试性 | 可降级性 | 可肯定性 | 可演示性 |
可信赖性 | 可部署性 | 可发现性 | 分布性 | 耐久性 |
有效性 | 高效性 | 可用性 | 可扩展性 | 故障透明性 |
容错性 | 保真性 | 灵活性 | 可检查性 | 可安装性 |
完整性 | 互通性 | 可学习性 | 可维护性 | 可管理性 |
移动性 | 可修改性 | 模块性 | 可操做性 | 正交性 |
可移植性 | 精确性 | 可预测性 | 过程能力 | 可生产性 |
可证性 | 可恢复性 | 相关性 | 可靠性 | 可重复性 |
重现性 | 弹性 | 响应性 | 可复用性 | 稳健性 |
安全 | 伸缩性 | 无缝性 | 自我维持性 | 可服务性 |
安全性 | 简单性 | 稳定性 | 标准合规性 | 可生存性 |
可持续性 | 可裁剪性 | 可测试性 | 及时性 | 可追溯性 |
在构建软件时,架构师必须明确哪些特征最重要。然而,许多因素是互相矛盾的。好比,让软件具有高性能的同时还要实现极大的伸缩性就很困难,由于实现这二者须要谨慎地平衡架构、运维及其余诸多因素。所以,在为架构设计作必要分析的同时,又要处理好各个因素之间不可避免的冲突,架构师在权衡每一个架构设计方案的利弊时,经常须要作出很是艰难的折中。近年来,软件开发核心工程实践的持续发展给咱们提供了条件,使咱们得以从新思考架构随时间的推移要如何变化,以及当这样的演进发生时,如何保护重要的架构特征。本书将这些部分联系起来,以一种新的方式思考架构和时间。安全
咱们想为软件架构添加一个新的标准“特征”——演进能力。服务器
不管咱们怎么努力,软件依然变得愈来愈难以改变。因为各类缘由,软件的组成部分不容易变动,并且随着时间推移变得愈发脆弱和难以操做。软件项目的变动一般是因为对功能或范围作了从新评估而致使的,可是还有一些变化是架构师和长期规划者没法控制的。尽管架构师喜欢为将来作战略性规划,但不断变化的软件开发环境使这一切变得困难重重。既然变化是必然的,那么咱们就只能因势利导地来利用它。网络
生物世界中,环境因天然因素和人为因素而不断变化。例如,20 世纪 30 年代初,澳大利亚的甘蔗受到甲虫危害,致使甘蔗做物严重减产,利润大减。1935 年 6 月,做为应对措施,当时的甘蔗实验站管理总局引入了甘蔗蟾蜍来捕食甲虫,这种蟾蜍本来只产于中美洲和南美洲。短暂喂养后,1935 年 7 月和 8 月在昆士兰州北部投放了甘蔗蟾蜍。由于它们的皮肤有剧毒,而且在当地没有天敌,很快这种蟾蜍就泛滥成灾了。现在,澳大利亚的甘蔗蟾蜍大约有 2 亿只。这件事告诉咱们:向高度动态的(生态)系统中引入变化,可能会产生没法预料的结果。架构
软件开发体系由全部的工具、框架、库以及最佳实践(软件开发领域的技术积累)构成。和生态系统同样,软件开发体系实现了平衡,开发人员可以理解这个体系并为其添砖加瓦。然而,这种平衡是动态的,随着新事物不断出现,平衡不断被打破和重建。想象一个脚踏独轮车,手里还拿着盒子的人。他是动态的,由于他须要不断调整来保持挺立;他又是平衡的,由于他保持着身体平衡。在软件开发体系中,每一项创新或新实践均可能打破现状,迫使系统从新创建平衡。就比如咱们不断地将更多的盒子抛向骑独轮车的人,迫使他不断寻求新的平衡。框架
架构师在不少方面都和这个倒霉的独轮车手类似,不断地平衡以适应环境变化。持续交付这项工程实践使得这个平衡过程有告终构性的转变,它将过去孤立的功能(例如运维)合并到了软件开发的生命周期中,这让咱们对变化的含义有了新的认识。企业级架构师不能再依赖静态的五年计划了,由于整个软件开发体系在不断变化,任何一个长期计划均可能变得毫无心义。
即使对经验丰富的实践者来讲,颠覆性的创新也是难以预测的。好比,像 Docker 这样的容器化技术的崛起就是一个不可预知的行业转变。但咱们仍然能够经过一系列小的演进找到一些蛛丝马迹。之前,操做系统、应用服务器和其余基础设施都是商品,须要斥巨资购买使用许可。当时许多架构设计着重于高效利用共享资源。渐渐地,Linux 变得足以支撑企业及应用,使得购买操做系统的费用降为零。接下来,经过 Puppet 和 Chef 等工具自动配置服务器的 DevOps 实践使得 Linux 运维工做也再也不须要成本。一旦开发环境免费并获得普遍应用,势必朝着更加通用和便携的方向发展,因而 Docker 应运而生。但若是没有以前全部的演进过程,容器化就不会发生。
咱们所使用的编程平台也在持续演进。新的编程语言提供了更好的应用编程接口(API),提升了对新问题的灵活性和适用性。新的编程语言还提供了不一样的范式和概念。例如,引入 Java 替代 C++,下降了编写网络代码的难度并改善了内存管理。回顾过去的 20 年,不少语言一直在持续改进它们的 API,与此同时,新的编程语言每每用于解决新的问题。编程语言的演变如图 1-2 所示。
图 1-2:流行编程语言的演进过程
不管是在软件开发的哪一个方面,好比编程平台、编程语言、运维环境、持久化技术等,咱们都知道改变会持续发生。虽然没法预测技术或领域格局什么时候会改变,或哪些变化会持续下去,但咱们清楚改变是不可避免的。所以,咱们应该在构建系统的过程当中对这一点保持清醒的认识。
若是整个体系持续地以出乎意料的方式发生变化,预测变化就变得不可能了,那么用什么来替代固定计划呢?企业架构师和开发人员必须学会适应变化。作长期计划有一个隐含的缘由是财务上的考虑,由于之前软件变动的成本很高。可是,现代工程实践经过自动化和其余先进实践(例如 DevOps)下降了软件变动的成本。
多年来,一些聪明的开发人员发现系统的某些部分相对而言更难修改。这即是将软件架构定义为“未来难以变动的部分”的缘由,这个省事的定义简单地区分了软件系统中真的难以修改的部分和能够轻松修改的部分。但这个定义依然难免走进了盲区,由于开发者预先假设“变动是困难的”,这变成了一个自证式的预言。
几年前,一些富有创新精神的架构师用新的视角审视了“未来难以变动”的问题。使架构具备可变性会怎样呢?换句话说,若是易于改变是架构的基本原则,那么变动将再也不困难。反过来,使架构具有演进能力会致使一组全新的行为出现,进而再次打破整个体系的平衡。
即便环境不改变,架构特征出现磨损该怎么办?架构师设计出架构,将其置于纷乱的现实世界,基于架构执行各项事务。架构师要如何保护他们定义的重要部分呢?
有一种不幸的退化叫做架构比特衰减,它在不少组织中均有发生。架构师选择特定的架构模式来知足业务需求及让系统具有某些能力,但这些特征经常意外地随着时间推移而退化。例如,架构师构建了一个包含顶部展示层、底部持久层和一些中间层的分层架构。负责报表功能的开发人员出于性能上的考虑常常会要求绕过中间层,直接从展示层访问持久层。架构师经过分层来隔离变化。然后开发人员绕过这些层,不只增长了耦合,还使得分层变得毫无价值。
定义了那些重要的架构特征后,架构师如何保护这些特征不磨损呢?答案是添加演进能力做为新的架构特征,使其在系统演进时保护其余特征。好比,架构师追求架构设计的高伸缩性,但不但愿在系统演进时削弱该特征。所以,演进能力是一种元特征和保护其余全部架构特征的架构封装器。
本书将阐明演进式架构的另外一个做用,即它是一种为架构的重要特征提供保护的机制。咱们探寻持续架构背后的理念。持续架构指构建架构的过程没有最终状态,它会随着软件开发体系的不断变化而演进,并保护重要的架构特征。咱们不会尝试定义整个软件架构,由于已经存在不少定义了。咱们经过引入时间和变化做为头等架构元素来扩展当前的定义。
咱们对演进式架构的定义以下。
演进式架构支持跨多个维度的引导性增量变动。
增量变动描述了软件架构的两个方面:如何增量地构建软件和如何部署软件。
在开发阶段,容许小的增量变动的架构更易于演进,由于对于开发者来讲,变动范围相对更小。对部署而言,增量变动指业务功能的模块化和解耦水平,以及它们是如何映射到架构中去的。示例以下。
假设有一个小工具商家 PenultimateWidgets,该商家采用微服务架构和一些现代工程实践运营着一个产品目录页面。该页面的一个功能是让用户给出小工具的星级评分。PenultimateWidgets 的其余一些业务也须要评分,好比客服表明、物流服务商评价等,因此这些业务共享星级评分服务。某天,该服务的开发团队发布了新版本,新版本容许用户给出半星评价(一个小而重要的升级)。其余须要评分的服务不须要强制升级,而能够逐渐地在合适的时候迁移到新版本。PenultimateWidgets 的 DevOps 实践采用了架构级别的监控,不只监控各个服务,还监控服务与服务之间的路由。当运营人员观察到在给定时间内没有请求路由至特定服务时,他们自动将该服务从体系中移除。
这是一个在架构级别进行增量变动的例子:若有须要,原服务和新服务能够并存。团队能够在适当(不太忙或必要)的时候完成迁移,当全部迁移都完成时,旧版本的服务将会做为垃圾被自动回收。
增量变动的成功须要一些持续交付实践的配合。并非任何状况都须要全部这些实践,但一般它们会一块儿发生。第 3 章将讨论实现增量变动的方法。
一旦架构师选择了重要的架构特征,他们会把变动引导进入架构,以保护这些重要特征。为此,咱们借用演化计算中的一个概念:适应度函数。该函数是一种目标函数,用于计算潜在的解决方案与既定目标的差距。在演化计算中,适应度函数决定一个算法是否在持续提高。换句话说,随着每一个算法变体的产生,基于设计者对算法“适应度”的定义,适应度函数决定每一个变体的“适应程度”。
对于演进式架构,随着架构的演进,咱们有着相似的需求。咱们须要评估机制,来评估变化对架构重要特征的影响,并防止这些特征随着时间的推移而退化。适应度函数的隐喻涵盖多种机制,包括度量、测试和其余检验工具。咱们采用这些机制来确保架构不会以不良方式变动。当架构师肯定了须要保护的架构特征时,他们会定义一个或多个适应度函数来提供保护。
以往,架构每每要划出一部分做为管理活动,最近架构师才接受了经过架构实现变动的思想。架构适应度函数容许在组织需求和业务功能的上下文中制定决策,并为明晰且可测试的决策奠基了基础。演进式架构并非毫无约束或不负责任的软件开发方式。相反,它能够在高速变迁的业务、严谨的系统需求和架构特征间找到平衡。适应度函数驱动架构设计决策,并引导架构变动适应业务和技术环境的变化。
第 2 章将详细介绍使用适应度函数指导架构演进。
不存在单独的系统。世界是一个总体。如何划分系统边界取决于讨论的主题。
——Donella H. Meadows
古希腊的物理学经过固定点分析宇宙,最终发展成了经典力学。可是到了 20 世纪初,随着仪器愈来愈精密,现象愈来愈复杂,人们逐渐从经典力学转向相对论。科学家意识到以前被视为孤立的现象实际上是相互影响的。自 20 世纪 90 年代起,受到启发的架构师愈来愈多地将软件架构视做多维的。持续交付也将运维归入了软件架构的范畴。软件架构师每每关注技术架构,但那只是软件项目的维度之一。若是架构师想构建可演进的架构,就必须考虑系统中全部会受变化影响的部分。正如物理学所讲的,万物都是相关联的,架构师深知软件项目是多维的。
为了构建能够不断演进的软件系统,架构师不能只考虑技术架构。例如,若是项目包含一个关系型数据库,那么数据库实体之间的结构和关系也会随着时间的推移而不断变化。另外,架构师也不但愿系统在演进过程当中暴露安全漏洞。这些例子展现了架构的不一样维度,它们一般在架构中以正交方式交织在一块儿。部分维度在常见的架构关注点范围以内(见表 1-1),可是架构维度的意义其实更普遍,它囊括了不少传统意义上技术架构范畴以外的东西。每一个项目都有许多维度,架构师在考虑架构演进时必需要想到。下面是一些影响现代软件架构演进能力的常见维度。
技术
架构中的实现部分:框架、依赖的库和实现语言。
数据
数据库模式、表格布局、优化计划等。一般由数据库管理员(DBA)处理这类架构。
安全
定义安全策略、指导方针和指定工具来帮助发现缺陷。
运维与系统
关注架构如何映射到现有的物理或虚拟的技术设施中,包括服务器、机器集群、交换机、云等。
以上每一个视角构成一个架构维度——为了支持特定视角而有意进行的划分。这里架构维度的概念涵盖了传统的架构特征和其余有助于构建软件系统的因素。随着问题和周遭环境的改变,咱们想保护架构不磨损,每一个维度都提供给咱们一个审视架构的视角。
从概念上划分架构的方法有不少,好比 IEEE 的软件架构定义中的 4+1 视图模型。它关注不一样角色的不一样视角,将整个系统划分红了逻辑视图、开发视图、进程视图和物理视图。在著名的《软件系统架构》一书中,做者给出了软件架构的视点目录,它涵盖了更多角色。相似地,Simon Brown 的 C4 建模符号也从概念上帮助组织区分关注点。不过,本书没有尝试建立任何维度分类,而是识别那些现有项目中的维度。从实用角度来看,不论如何对关注点进行分类,架构师都须要保证这些维度不磨损。不一样的项目有不一样的关注点,这致使每一个项目都有特定的维度。对于新项目,以上任何技术都能提供有用的看法,可是对于现有的项目,咱们必须处理眼前的实际状况。
按照架构的维度思考,经过评估重要维度对变化的响应,架构师能够分析不一样架构的演进能力。随着系统与互相冲突的问题(伸缩性、安全性、分布式、事务性等)关联得愈来愈紧密,架构师必须跟踪更多的维度。只有结合全部这些重要维度,思考系统将如何演进,才能构建出能够不断演进的系统。
项目的整个架构范围由软件需求和其余维度构成。当架构和整个体系随着时间的推移一块儿演进时,咱们可使用适应度函数来保护架构特征,如图 1-3 所示。
图 1-3:架构由需求和其余维度构成,每一个维度都受适应度函数保护
在图 1-3 中,架构师肯定了可审计性、数据、安全性、性能、合法性和伸缩性是该应用的关键架构特征。随着业务需求不断变化,每一个架构特征都经过适应度函数来保护其完整性。
咱们强调架构总体的重要性,但也应意识到,技术架构模式及相关议题也是架构演进的很大一部分,好比耦合和内聚。第 4 章将讨论技术架构耦合对演进能力的影响,第 5 章将讨论数据耦合的影响。
耦合不仅和软件项目的结构元素有关。近来,不少软件公司认识到了团队结构对架构的影响。咱们将讨论软件中的各种耦合因素,可是团队结构的影响出现得如此早且频繁,咱们须要立刻就此展开讨论。
1968 年 4 月,梅尔文 • 康威在《哈佛商业评论》上发表了一篇名为“How Do Committees Invent?”的论文。在这篇论文中,康威提出:社会结构,特别是人与人之间的沟通途径,将不可避免地影响最终的产品设计。
康威描述道,在设计的最初阶段,人们首先须要高瞻远瞩地思考如何将职责划分为不一样的模式。团队分解问题的方式会左右他们以后的选择,这即是康威定律。
在设计系统时,组织所交付的方案结构将不可避免地与其沟通结构一致。**
——梅尔文 • 康威
正如康威所描述的,当技术人员将问题分解成更小的块,使其更易于委派时,就会产生协调问题。不少组织为了解决协调问题,会设置正式的沟通结构或是创建森严的等级制度,但这样的解决方案每每是僵化的。好比,按照技术职能(用户界面、业务逻辑等)划分的团队,他们在处理常见的跨职能问题时协调的开销就会增长。那些从创业团队跳槽到跨国公司工做的人,极可能会经历两种大相径庭的文化:前者很是灵活且适应性很强,然后者的沟通结构每每是僵化的。康威定律一个很好的例子就是修改两个服务间的契约。若是一方想修改服务,而这须要另一方的赞成和配合,那么这件事就可能变得很难。
在论文中康威特别提醒软件架构师,不要只关注软件架构和设计,还应关注团队之间委派、分配和协调工做的方式。
在不少组织中,团队是根据职能来划分的。示例以下。
前端开发团队
负责用户界面(UI)技术(如 HTML、移动端和桌面端)。
后端开发团队
专门构建后端服务(有时还包括 API 层)。
数据库开发团队
专门构建存储和逻辑服务。
在这样的组织中,管理层从人力资源的角度简单地按照职能划分团队,没有充分考虑工程效率。虽然每一个团队都有其擅长的领域(好比构建一个视图,增长一个后端 API 或服务,或者开发一个新的存储机制),可是当须要发布新的业务功能或特性时,三个团队都要参与其中。各个团队一般都会针对眼前的任务优化效率,而不是针对那些更抽象的战略业务目标(特别是有工期压力时)。这会致使各团队每每专一于交付各自的组件,而不关注端到端的特性价值,致使这些组件可能没法高效协做。
在这样的团队编制下,因为每一个团队都在不一样的时间忙于本身的组件,所以那些依赖全部团队的特性须要花费更长的时间。例如,修改目录页这样常见的业务变动涉及 UI、业务规则和数据库模式的变动。若是每一个团队都各自为战,那么他们必须协调时间表,这将增长实现该特性所需的时间。这个例子很好地解释了团队结构对架构和演进能力的影响。
康威在论文里提到:“每当新的团队组建,其余团队的职责范围会缩小,可以有效执行的可选设计方案也会随之变少。”换句话说,人们很难改变其职责范围外的事情。软件架构师须要时刻关注团队的分工模式,从而使架构目标和团队结构保持一致。
不少构建架构(如微服务)的公司围绕服务边界构建团队,而不是按孤立的技术架构来划分。在 ThoughtWorks 技术雷达中,咱们称之为“康威逆定律”。以这种方式组织团队是理想的,由于团队结构会影响软件开发的不少维度,而且会反映问题的大小和范围。好比,在构建微服务架构时,企业一般会构建与架构相仿的团队结构,经过打破功能筒仓使每一个团队都有人能考虑到业务的各个角度和技术的方方面面。
构建与目标系统架构相仿的团队结构,这样项目会更容易实现。
PenultimateWidgets 及其逆康威时刻
本书以 PenultimateWidgets 公司为例。它是排行倒数第二的小工具经销商,仍是一家大型在线小工具(各类小商品)商家。这家公司正在更新其大部分 IT 基础设施,包括一些想暂时保留一段时间的遗留系统和正在迭代开发中的新战略系统。本书将重点介绍 PenultimateWidgets 所遇到的问题以及采起的解决方案。
他们的架构师首先观察了软件开发团队。旧的单体应用采用了分层架构,将展示、业务逻辑、持久化和运维分离开来。而他们的团队设置也和架构一模一样:全部前端开发人员在一块儿工做,后端开发人员和数据库管理员也各自工做,运维则外包给了第三方。
当开发人员开始在架构(基于具备细粒度服务的微服务架构)上工做时,协调成本急剧增长。由于服务是围绕领域(例如用户结算)而不是技术架构来构建的,因此在变动单个领域时将须要各团队间进行大量的协调。
因而,PenultimateWidgets 采起了康威逆定律,创建了和服务范围相匹配的跨职能团队:每一个服务团队包括服务负责人、几位开发人员、一位业务分析师、一位DBA、一位质量保障人员和一位运维人员。
本书多处提到了团队的影响,并会经过示例说明它带来的结果。
对于演进式架构,一个常见的问题是关于其名称的:为何叫做演进式架构而不是别的?好比增量式、持续式、敏捷式、响应式或应急式,等等。这些术语都不够准确达意。这里所说的演进式架构包含了两个关键特征:增量和引导。
持续式、敏捷式和应急式都表达了随时间不断变化的概念,这确实是演进式架构的关键特征,可是这些术语都没能准确地表达出架构将如何变化,或者说指望的架构最终是什么样子的。虽然这些术语都隐含着环境变化,可是都没能涵盖架构应有的样子。而在演进式架构的定义中,引导的含义反映了咱们想实现的架构,即咱们的最终目标。
相比可适应这个词,咱们倾向于演进式,由于咱们对经历根本性演变的架构感兴趣,而不是那些经历了修修补补后变得愈来愈难以理解、复杂问题频发的架构。适应意味着解决方案不求优雅或长久,只要能找到方法使系统运做就行。为了构建能实实在在演进的架构,架构师必须支持真正的变化,而不是权宜之计的考虑。回到咱们的生物学隐喻,演进是这样一个过程:创建一个适用的并能在其所处的不断变化的环境中持续运行的系统。系统可能存在个别的适应性,可是架构师应该关心总体可演进的系统。
演进式架构主要由三方面构成:增量变化、适应度函数和适当的耦合。本书余下的部分将分别讨论它们,而后再将它们结合起来,帮助咱们构建和维护支持不断变化的架构。