![](https://b3logfile.com/bing/20181205.jpg?imageView2/1/w/960/h/540/interlace/1/q/100)
第0章 序
架构是不断努力的结果,是一个与开发工作紧密结合的过程,这样他才能同时响应不断变化的需求和开发人员的反馈。我们称之为“演进式架构”,正是要强调当无法预测变化时,该架构仍然可以朝着正确的方向发展。
构建演进式架构的核心是采取小步变更,然后通过反馈循环让团队的每个成员不断地从系统的发展过程中学习。
第 1 章 软件架构
架构是的工作就是理解和权衡那些“重要的东西”(无论他们是什么)。
持续架构指构建架构的过程没有最终状态,它会随着软件开发系统的不断变化而演进,并保护重要的架构特征。演进式架构支持扩多个维度的引导性增量变更。
增量变更描述了软件架构的两个方面:如果增量的构建软件和如何部署软件。
多个架构维度:
不存在单独的系统。世界是一个整体。如何划分系统边界取决于讨论的主题。
影响现代软件架构演进的常见维度:
技术:结构中的实现部分,框架、依赖的库和实现语言。
数据:数据库模式、表格布局、优化计划等,
安全:定义安全策略,指导方针和指定工具来帮助发现缺陷。
运维与系统:关注架构如何映射到现有的物理或虚拟的技术设施中,包括服务器、机器集群、交换机、云等。
康威定律
- 社会结构,特别是人与人之间的沟通途径,将不可避免地影响最终的产品设计。
- 在设计的最初阶段,人们首先需要高瞻远瞩地思考如何将职责划分为不同的模式。团队分解问题的方式会左右他们之后的选择,这便是康威定律。
- 在设计系统时,组织所交付的方案结构将不可避免的与其沟通结构一致。
- 人们很难改变其职责范围外的事情。软件架构师需要时刻关注团队的分工模式,从而使架构目标和团队结构保持一致。
如何演进?
- 演进式架构包含了两个关键特征:增量和引导。引导的含义反应了我们想要实现的架构,即我们的最终目标。
- 回到生物学的隐喻,演进是这样一个过程:建立一个使用的并能在其所处的不断变化的环境中持续运行的系统。系统可能存在个别的适应性,但是架构师应该关心整体可演进的系统。
第 2 章 适应度函数
演进式架构是支持跨多个维度进行引导性增量变更的架构。
系统不是其组成部分的总和,而是各部分相互作用的产物。
所谓“适应度函数引导演进式架构”,指的是通过单独的适应度函数评估单个架构选择,同时通过全系统适应度函数确定变更的影响。适应度函数共同指出架构中对我们重要的部分,使我们能够在软件开发过程中做出各种关键又令人烦恼的权衡。
监控驱动开发(MDD),通过监控生产环境来评估技术和业务的监控程度,而不是仅仅依赖测试。这些持续适应度函数比标准的触发式测试更为动态。
对于任何软件系统,团队都应该尽早确定最重要的适应度函数及其优先次序。这有助于架构师将大型系统拆解成更小的系统,使每个系统对应较少的适应度函数。适应度函数可以分为三类:
- 关键维度,对做出技术决策或设计决策至关重要。
- 相关维度,需要在特性级别考虑,但是不太会指导架构决策。
- 不相关的维度,不会影响设计和技术决策。
第 3 章 实施增量变更
演进式架构是支持跨多个维度进行引导性增量变更的架构。
可测试性(架构特征是否能够通过自动化测试验证其正确性)是软件架构中一个被经常忽略的特性。
一旦架构师确定了适应度函数,就应该确保及时地对其进行评估。自动化是持续性评估的关键。
开发人员越早发现问题,那么解决问题的成本将越低。目标冲突无法避免,但是,尽早发现和量化这些冲突可以使架构师做出更明智的决定,制定出更清晰的目标和原则。
第 4 章 架构耦合
演进架构注重适当的耦合,即如何确定哪些架构维度间应该相互耦合以最小的开销和成本最大程度的获益。
模块化描述了相关代码的逻辑分组,可以以不同的物理方式封装模块。组件就是模块的物理封装。模块意味着逻辑分组,而组件意味着物理划分。、
所有模块机制都有助于代码复用,在任何级别尝试复用代码都是明智的选择,无论是单一函数,还是封装好的业务平台。
领域驱动设计(DDD)定义了界限上下文,所有领域相关内容在该领域内是可见的,但不对其他界限上下文可见。
构建演进式架构的关键之一在于决定自然组件的粒度以及他们之间的耦合,以此来适应那些通过软件架构支持的能力。
分层架构的主要设计准则是将不同的技术能力分隔到不同的层,每层职责各异。这种架构的主要优点是关注点独立且分离。每一层相对于其他层都是独立的,但能通过明确定义的接口互相访问。这使得对某一层的变更不会影响其他层,同时将相似的代码组织到一起,为该层的专业化和分离提供了空间。
当前微服务架构是单体架构常见的迁移目标,但相比于单体架构,它在很多方面都更复杂,如服务、数据粒度、运维、协调、事务等。如果开发团队难以构建最简单的架构,那么转向更复杂的架构又如何能解决问题呢?如果无法构建单体应用,为什么你认为微服务能解决问题呢?
微服务架构通常遵循七个原则:
-
围绕业务领域建模。微服务设计的重点是基于业务领域,而不是基于技术架构。因此,架构量子反应了界限上下文。
-
隐藏实现细节。微服务的技术架构封装在基于业务领域的服务边界中。
-
自动化文化。
-
高度去中心化。微服务形成一种无共享架构,其目标是尽可能减少耦合。
-
独立部署。
-
隔离失败。开发人员会在微服务上下文中和服务间的协调中隔离失败。
-
高度可观察。如果运维人员无法监控某个服务,那么它就相当于不存在了。
微服务的主要目标是通过物理界限上下文来隔离领域及理解问题领域。因此,它的架构量子就是服务,这使得它成为了演进式架构的优秀示例。
关于架构,没有所谓的“正确”的观点,它只是反映了开发人员构建项目的目标。如果仅关注技术架构,那么跨维度的变更会更加容易。然而,一旦忽略了领域视角,那么跨领域维度的演进将和大泥团架构差不多。
基于服务的架构,和微服务的三个区别:
- 更大的服务粒度。其服务粒度更像一个“单体应用”。
- 数据库作用域,往往使用单体数据库。
- 集成中间件。通过中介(如服务总线)来进行外部协调。
- BaaS(后端即服务)是那些明显或从根本上依赖于第三方应用或云端服务的应用。Faas(功能即应用),服务由功能提供商所定义的事件类型触发
确保架构与问题领域相匹配。不要尝试强行使用不合适的架构。
不同类型的架构:
-
大泥团架构。
-
单体架构。
- 非结构化的单体架构
- 分层架构
- 模块化的单体架构
-
事件驱动架构(EDA)。
- 代理模式
- 中介模式。
-
服务导向架构(SOA)。
- 企业服务总线驱动的 SOA
- 微服务架构
- 基于服务的架构
-
“无服务”架构。
- BaaS
- Faas
第 5 章 演进式数据
数据库模式是抽象的,和类的层次结构类似。当现实世界发生变化,这些变化需要反映在开发人员和 DBA 所建立的抽象当中。否则,抽象将逐渐脱离与现实世界的同步。
数据库设计演进的关键在于数据库模式和代码演进,开发人员必须以和代码变更相同的方式处理数据库结构的变更,他们必须是经过检验的、版本化的和增量的。
虽然系统无法避免事务,但架构师应尽可能地限制事务上下文,因为他们形成了紧密的耦合结,妨害了在不影响的情况下变更组件或服务的能力。更重要的是,架构师在考虑架构变更时应将事务边界考虑在内。
不要仅按字面意义来理解微服务,对每个服务而言,小并不是必需的,能捕获有用的界限上下文才是关键。
第 6 章 构建可演进的架构
演进机制:通过三步构建演进式架构
- 识别受演进影响的架构维度。架构师必须确定在演进中他们想保护的架构维度。其中一定包含技术架构,通常还有数据设计、安全、可伸缩性和其他一些他们认为重要的特征。
- 为每个维度定义适应度函数。为了保证代码的架构特征,架构师通常将一系列代码衡量直白哦构建到部署流水线中,例如,避免组件循环依赖。
- 使用流水线自动化适应度函数。架构是必须在项目中推进增量变更,在部署流水线中定义不同的阶段来执行适应度函数并管理部署实践,例如环境准备、测试和其他 DevOps。
改良现有架构:赋予现有架构演进能力取决于三个因素:组件耦合度、工程实践成熟度,以及开发人员构建适应度函数的难度。
适当的耦合和内聚。功能内聚性决定了组件重构后的最终粒度。选择架构前,需要理解面临的业务问题。
- 工程实践。虽然持续交付实践无法保证架构能实现演进,但它仍然不可或缺。
- 适应度函数。适应度函数是演进式架构的保护层。
- 不要仅仅因为元工作有趣而构建架构。
在分解单体架构时,确定正确的服务粒度是关键。创建大型服务能够缓解一些问题,但是无法将单体应用分解到更小的粒度。粒度过小的组件则会导致过多的服务编排和消息通信所带来的开销以及服务间的相互依赖。为了迈出架构迁移的第一步,开发人员需要定义新的服务边界。
- 业务功能分组
- 事务边界
- 部署目标。增量变更使得开发人员可以按照不同的计划有选择地发布代码。
共享就是耦合的一种,在微服务中是不可取的。我们用复制作为替代方案来避免使用共享的库。
计算机领域的任何问题都可以通过增加一个间接的中间层来解决,除非该问题是由中间层太多导致的。
与其一次性完成整个迁移,更加实用的方法是循序渐进地将单体应用分解成服务,关注事务边界、结构耦合和其他固有特征来创建若干架构重建迭代。优先将单体应用分解成几大块,修复集成点,然后清理和重复这个过程。逐步迁移是微服务领域的首先迁移方式。
演进架构构建指南:
- 去除不必要的可变性。稳定性是持续交付的目标之一,即在已知运行良好的系统上进行构建。构建不可变的基础设施这一现代 DevOps 视角提现了这一目标。不可变的基础设施是指完全以程序的方式定义的系统。所有对系统的
- 变更都必须通过代码完成,而不是修改运行中的操作系统。让决策可逆。积极演进的系统不可避免地会意外失败。蓝绿部署,功能开关(金丝雀发布)。
- 演进优先于预测。未知的未知问题是软件系统的大敌。
构建防腐层。当处于“最后责任时刻”时,为自己几个问题,例如:“我现在就必须做决定么?”“能否部延误工作的情况下晚点做决定?”“我可以做什么来应付当前的需求并且之后能轻松加以调整?”控制应用中的耦合点,特别是外部资源,是架构师的关键职责之一。
服务模板是保证一致性的常见方案。其中包含一系列预配置的公共基础设施库,如服务复现、监控、日志等。使用服务模板仅将合适的架构部分耦合在一起,例如基础设施组件团队可以从耦合中获益。
构建可牺牲架构。在管理上,不应该询问是否该构建一个试验性的系统然后将其抛弃。因为你一定会那样做。因此,要做好抛弃它的计划,因为你终将如此。可牺牲性架构:在概念验证成功后即将被抛弃的架构。
应对外部变化。
当我们依赖第三方代码时,开发人员必须采取防御措施来预防可能的意外,例如破坏性的变更,未经通知的删除等你。
铭记外部依赖在带来好处的同时还需要付出成本,不要让外部力量影响构建的稳定性。
以拉取的方式获得外部依赖是开启依赖管理的良好开端。
第 7 章 演进式架构的陷阱和反模式
反模式:不好的模式。软件的反模式包含两层含义,首先,反模式是一种实践,开始看起来不错,但结果证明是错的。其次,大多数反模式都是有更好的替代方案。
技术架构领域
- 反模式:供应商为王。
无论从技术还是从业务流程的角度来看,将外部工具或框架置于架构的核心会严重限制架构的演进能力。
不要将你的架构耦合,使得供应商为王。将供应商的产品视为集成点,在集成点间构建防腐层。
- 陷阱:抽象泄露
底层抽象破坏会导致意外的灾难,即原始抽象泄露。
始终保持对当前抽象层以下至少一个抽象层的完全理解。
- 反模式:最后 10%的陷阱
所有的项目都存在缺憾。
无论开发人员多么努力,他们都无法将事物提炼得足够精细,这便是无限回归问题的一部分:一些命题依赖于其他命题而成立,没有止境。
在软件领域中,无限回归表现为人们想要用终极的细节详细描述任何事务,但在任何现有细节之下总是存在另一层更细粒度的细节。
- 反模式:代码复用和滥用
代码的易用性和复用性往往成反比,当开发人员构建可复用的代码时,他们必然会为了将来开发人员以各种方式使用代码添加特性。所有针对未来的特性都使得开发人员更难将代码用于单一目的。
微服务避免代码复用,遵循重复优于耦合的理念。该理念认为复用意味着耦合,因此微服务架构是极度解耦的。
然而,微服务的目标并不是追求重复,而是隔离领域内的实体。
复用代码可以是资产,也可能是潜在的责任。我们要确保代码中引入的耦合点不会和其他架构目标产生冲突。
当耦合点妨碍了演进或其他重要的架构特征时,通过分叉或重复来打破耦合点。
- 陷阱:简历驱动开发
为了这些知识丰富自己的简历而选择框架和库。
不要为了架构而构建架构,构建架构是为了解决问题
在选择架构前,要始终解决问题,不要本末倒置。
增量变更
- 反模式:管理不当
软件架构并非处于真空之中,它通常反映了设计时所处的环境。十年前,操作系统、基础设施都是非常昂贵的商品,为了应对这些现实世界的压力,架构师通过设计架构来最大程度地共享资源。
在现代环境,将不同技术栈统一成单一技术栈是不当的管理。这会无意将问题过度复杂化,管理决策使得使得实现解决方案所需要的工作毫无意义地成倍增加。
在微服务架构中,由于服务间不存在技术架构或数据架构的耦合,不同的团队可以选择正确的复杂度来实现其服务。其终极目标是化繁为简,保持技术栈复杂度和技术需求的一致。
金发姑娘管理模式:选择简单、中等、复杂三种技术栈作为标准,然后允许单个服务需求驱动技术栈的需求。这样就赋予了团队选择合适技术栈的灵活性,还能继续为企业保留标准化带来的好处。
- 陷阱:发布过慢
如果企业围绕持续部署打造工程文化,期望所有变更在通过了部署流水线设置的挑战后变进入生产环境,那么开发人员就会习惯于持续变更。
持续交付跟进生产周期,即启动和完成单位工作所用的时间。生产周期从开发然元着手开发某个新功能起开始计时,当该功能在生产环境中运行时停止计时。其目的是衡量工程效率,持续交付的关键目标之一便是缩短生产周期。
更快的生产周期意味着架构可以更快地演进,因此,一个项目的生产周期决定了架构的演进速度,换句话说,演进速度和生产周期成正比。
良好的工程,部署和发布实践是使演进式架构获得成功的关键,反过来通过假设驱动开发为业务提供新能力。
业务问题
- 陷阱:产品定制
销售人员希望软件可以无限定制,然后,这种能力需要一系列实施技术为支撑。版本控制分支、标签技术来跟踪版本、永久的功能开关、通过 UI 来完成定制。
定制会妨碍演进能力,企业应该实事求是地评估相关成本。
- 反模式:报表
从架构的角度来看,协调也是一种耦合,使用最终一致性能让架构师免于协调,为应用程序的不同用途做成不同的抽象。
- 陷阱:规划视野
人们对某件事情投入的时间和精力越多,就越难放弃它。在软件中,表现为不合理的工件附件。例如,人们在规划和文档上投入的时间和精力越多,就越可能保护其中的内容,即便有证据表名他们不准确或过时了
在通过最终用户反馈验证所用技术确实适用他们试图解决的问题之前,架构师应该避免在实际构建软件之前采用需要大量前期投入的技术。
第 8 章 实践演进式架构
以领域为中心的团队,其目标是消除运营摩擦。换句话说,团队拥有负责设计、实现和部署的所有角色。全功能的团队的目标之一便是消除协调摩擦。
通过围绕领域组件架构和团队,现在可以由同一个团队处理常见的变更单元,从而减少团队间的摩擦。以领域为中心的架构仍然使用分层架构来发挥其优势,如关注点分离。比如,某个微服务的时间或许依赖与分层架构的框架,使得团队可以轻松替换某个技术层。微服务将技术架构封装在领域范围内,颠覆了传统的关系。
构建全功能团队可以防止不同团队间的互相职责,并让团队产生主人翁意识,激励团队成员做到最好。
构建小型团队是为了减少沟通连接。每个团队无须了解其他团队所做的事情,除非团队之间存在集成点,也应该使用适用度函数保证集成点的完整性。
试验文化。成功的演进离不开试验。成功的试验是经常进行一些小型活动来尝试新的想法并将成功的试验集成到现有的系统中。鼓励试验的方式:从外部吸收想法;鼓励明确的改进;进行探针试验并稳定下来;创造创新时间;采用基于集合的开发方式;连接工程师和最终用户。
新一代架构的出现速度与生产周期成正比,架构量子越小,生产周期越短。
对演进式架构的增量变更而言,测试是关键的组件,并且适用度函数也在积极的利用测试。
将现有架构转变成可演进的架构的第一步是规模化。因此,开发人员的首要任务是研究当前项目是否具有模块化,并围绕这些发现重建架构。