微服务架构设计模式笔记--第二章 服务的拆分策略

1. 微服务架构到底是什么

第1章描述了微服务架构的关键思想是如何进行功能分解。你可以将应用程序构建为组服务,而不是开发一个大型的单体应用程序。一方面,将微服务架构描述为一种功能分解是有用的。但另一方面,它留下了几个未解决的问题,包括:微服务架构如何与更广泛的软件架构概念相结合?什么是服务?服务的规模有多重要?
为了回答这些问题?我们需要退后一步,看看软件架构的含义。软件的架构是一种抽象的结构,它由软件的各个组成部分和这些部分之间的依赖关系构成。正如你将在本节中看到的,软件的架构是多维的,因此有多种方法可以对其进行描述。架构很重要的原因是它决定了应用程序的质量属性或能力。传统上,架构的目标是可扩展性、可靠性和安全性。但是今天,该架构能够快速安全地交付软件,这一点非常重要。你将了解微服务架构是一种架构风格,可为应用程序提供更高的可维护性、可测试性和可部署性。
我将通过描述软件架构的概念及其重要性来开始本节。接下来,我将讨论架构风格的概念。然后我将微服务架构定义为特定的架构风格。让我们从理解软件架构的概念开始。

1.1 软件架构是什么,为什么它如此重要

计算机系统的软件架构是构建这个系统所需要的一组结构,包括软件元素、它们之间的关系以及两者的属性。
Bass等著《 Documenting Software Architectures: Views and Beyond》

这显然是一个非常抽象的定义。但其实质是应用程序的架构是将软件分解为元素(element)和这些元素之间的关系( relation)。由于以下两个原因,分解很重要:

  • 它促进了劳动和知识的分工。它使具有特定专业知识的人们(或多个团队)能够就应用程序高效地协同工作。
  • 它定义了软件元素的交互方式。将软件分解成元素以及定义这些元素之间的关系,决定了软件的能力。

应用程序有两个层面的需求。

  1. 功能性需求:这些需求决定一个应用程序做什么。
    这些通常都包含在用例(use case)或者用户故事(user story)中。应用的架构其实跟这些功能性需求没什么关系。功能性需求可以通过任意的架构来实现,甚至是非常糟糕的大泥球架构。
  2. 非功能性需求:架构的重要性在于,它帮助应用程序满足了第二类需求。我们把这类需求也称之为质量属性需求,或者简称为“能力”。这些非功能性需求决定一个应用程序在运行时的质量,比如可扩展性和可靠性。它们也决定了开发阶段的质量,包括可维护性、可测试性、可扩展性和可部署性。为应用程序所选择的架构将决定这些质量属性。

1.2 架构风格

特定的架构风格提供了有限的元素(组件)和关系(连接器),你可以从中定义应用程序架构的视图。应用程序通常使用多种架构风格的组合。微服务架构将应用程序构造为一组松散耦合的服务。

六边形架构风格

在这里插入图片描述
六边形架构风格的一个重要好处是它将业务逻辑与适配器中包含的表示层和数据访问层的逻辑分离开来。业务逻辑不依赖于表示层逻辑或数据访问层逻辑
由于这种分离,单独测试业务逻辑要容易得多。另一个好处是它更准确地反映了现代应用程序的架构。可以通过多个适配器调用业务逻辑,每个适配器实现特定的API或用户界面。业务逻辑还可以调用多个适配器,每个适配器调用不同的外部系统。六边形架构是描述微服务架构中每个服务的架构的好方法。

微服务架构强加的一个关键约束是服务松耦合。因此,服务之间的协作方式存在一定限制。为了解释这些限制,我将尝试定义什么是服务,解释松耦合意味着什么,并告诉你为什么这很重要。

什么是服务

在这里插入图片描述

服务是一个单一的、可独立部署的软件组件,它实现了一些有用的功能。图中显示了服务的外部视图,在此示例中是 Order Service。服务具有API,为其客户端提供对功能的问。有两种类型的操作:命令和查询。API由命令、查询和事件组成。命令如createorder()执行操作并更新数据。查询,如 findorderByid()检索数据。服务还发布由其客户端使用的事件,例如 Ordercreated服务的API封装了其内部实现。与单体架构不同,开发人员无法绕过服务的API直接访问服务内部的方法或数据。因此,微服务架构强制实现了应用程序的模块化。
微服务架构中的每项服务都有自己的架构,可能还有独特的技术栈。但是典型的服务往往都具有六边形架构。其API由与服务的业务逻辑交互的适配器实现。操作适配器调用业务逻辑,事件适配器对外发布业务逻辑产生的事件。

什么是松耦合

微服务架构的最核心特性是服务之间的松耦合性。服务之间的交互采用API完成,这样做就封装了服务的实现细节。这允许服务在不影响客户端的情况下,对实现方式做出修改。松耦合服务是改善开发效率、提升可维护性和可测试性的关键。小的、松耦合的服务更容易被理解、修改和测试。
我们通过API来实现松耦合服务之间的协调调用,这样就避免了外界对服务的数据库的直接访问和调用。服务自身的持久化数据就如同类的私有属性一样,是不对外的。保证数据的私有属性是实现松耦合的前提之一。这样做,就允许开发者修改服务的数据结构,而不用提前与其他服务的开发者互相协商。这样做在运行时也实现了更好的隔离。例如,一个服务的数据库加锁不会影响另外的服务。但是你稍后就会看到在服务间不共享数据库的弊端,特别是处理数据一致性和跨服务查询都变得更为复杂。

共享类库的角色

开发人员经常把一些通用的功能打包到库或模块中,以便多个应用程序可以重用它而无须复制代码。毕竟,如果没有 Maven或npm库,我们今天的开发工作都会变得更困难。你可能也想在微服务架构中使用共享库。从表面上看,它似乎是减少服务中代码重复的好方法。但是你需要确保不会意外地在服务之间引入耦合。
例如,想象一下多个服务需要更新 Order业务对象的场景。一种选择是将该功能打包为可供多个服务使用的库。一方面,使用库可以消除代码重复。另一方面,如果业务需求的变更影响了order业务对象,开发者需要同时重建和重新部署所有使用了共享库的服务。更好的选择是把这些可能会更改的通用功能(例如 Order管理)作为服务来实现,而不是共享库。
你应该努力使用共享库来实现不太可能改变的功能。例如,在典型的应用程序中,在每个服务中都实现一个通用的 Money类(例如用来实现币种转换等固定功能)没有任何意义。相反,你应该创建一个供所有服务使用的共享库。

2. 为应用程序定义微服务架构

那么如何定义一个微服务架构呢?跟所有的软件开发过程一样,一开始我们需要拿到领域专家或者现有应用的需求文档。跟所有的软件开发一样,定义架构也是一项艺术而非技术。本节我们将介绍一种定义应用程序架构的三步式流程。
在这里插入图片描述

  • 第一步 将应用程序的需求提炼为各种关键请求。
  • 第二步 确定如何分解服务。
  • 第三步 确定每个服务的API。

2.1 识别系统操作

定义应用程序架构的第一步是定义系统操作。起点是应用程序的需求,包括用户故事及其相关的用户场景。图中所示的两步式流程识别和定义系统操作。
在这里插入图片描述

  • 第一步 创建由关键类组成的抽象领域模型,这些关键类提供用于描述系统操作的词汇表。
  • 第二步 确定系统操作,并根据领域模型描述毎个系统操作的行为。

领域模型主要源自用户故事中提及的名词,系统操作主要来自用户故事中提及的动词。你还可以使用名为事件风暴的技术定义领域模型。

创建抽象领域模型

定义系统操作的第一步是为这个应用程序描绘一个抽象的领域模型。注意这个模型比我们最终要实现的简单很多。创建领域模型会采用一些标准的技术,例如通过与领域专家沟通后,分析用户故事和场景中频繁出现的名词。

定义系统操作

当定义了抽象的领域模型之后,接下来就要识别系统必须处理的各种请求。前端的用户界面向后端的业务逻辑发出请求,后端的业务逻辑进行数据的获取和处理。
有以下两种类型的系统操作:

  • 命令型:创建、更新或删除数据的系统操作。
  • 查询型:查询和读取数据的系统操作。

抽象的领域模型和系统操作能够回答这个应用“做什么”这一问题。这有助于推动应用程序的架构设计。每一个系统操作的行为都通过领域模型的方式来描述。每一个重要的系统操作都对应着架构层面的一个重大场景,是架构中需要详细描述和特别考虑的地方。
现在我们来看看如何定义应用程序的微服务架构。

2.2 根据业务能力进行服务拆分

创建微服务架构的策略之一就是采用业务能力进行服务拆分。业务能力是一个来自于业务架构建模的术语。业务能力是指一些能够为公司(或组织)产生价值的商业活动。特定业务的业务能力取决于这个业务的类型。例如,保险公司业务能力通常包括承保、理赔管理账务和合规等。在线商店的业务能力包括:订单管理、库存管理和发货,等等。

  • 业务能力定义了一个组织的工作
    组织的业务能力通常是指这个组织的业务是做什么,它们通常都是稳定的。与之相反,组织采用何种方式来实现它的业务能力,是随着时间不断变化的。
  • 识别业务能力
    一个组织有哪些业务能力,是通过对组织的目标、结构和商业流程的分析得来的。每一个业务能力都可以被认为是一个服务,除非它是面向业务的而非面向技术的。例如,保险承保能力的输人来自客户的应用程序,这个业务能力的输出是完成核保并报价。
  • 从业务能力到服务
    一旦确定了业务能力,就可以为每个能力或相关能力组定义服务。

2.3 根据子域进行服务拆分

Eric evans在他的经典著作中( Addison- Wesley Professional,2003)提出的领域驱动设计是构建复杂软件的方法论,这些软件通常都以面向对象和领域模型为核心。领域模型以解决具体问题的方式包含了一个领域内的知识。它定义了当前领域相关团队的词汇表,DDD也称之为通用语言(Ubiquitous language)。领域模型会被紧密地映射到应用的设计和实现环节。
在微服务架构的设计层面,DDD有两个特别重要的概念,子域限界上下文
传统的企业架构建模方式往往会为整个企业建立一个单独的模型,DDD则采取了完全不同的方式。在这样的模型中,会有适用于整个应用全局的业务实体定义,例如客户或订单。这类传统建模方式的挑战在于,让组织内的所有团队都对全局单一的建模和术语定义达成一致是非常困难的。另外,对于组织中的特定团队而言,这个单一的业务实体定义可能过于复杂,超出了他们的需求。此外,这些传统的领域模型可能会造成混乱,因为组织内有些团队可能针对不同的概念使用相同的术语,而也有些团队会针对同一个概念使用不同的术语。
DDD通过定义多个领域模型来避免这个问题,每个领域模型都有明确的范围。领域驱动为每一个子域定义单独的领域模型。子域是领域的一部分,领域是DDD中用来描述应用程序问题域的一个术语。识别子域的方式跟识别业务能力一样:分析业务并识别业务的不同专业领域,分析产出的子域定义结果也会跟业务能力非常接近。FTGO的子域包括:订单获取、订单管理、餐馆管理、送餐和会计。正如你所见:这些子域跟我们之前定义的业务能力非常接近。
DDD把领域模型的边界称为限界上下文( bounded context)。限界上下文包括实现这个模型的代码集合。当使用微服务架构时,每一个限界上下文对应一个或者一组服务。换一种说法,我们可以通过DDD的方式定义子域,并把子域对应为每一个服务,这样就完成了微服务架构的设计工作。图中展示了子域和服务之间的映射,每一个子域都有属于它们自己的领域模型。
在这里插入图片描述
DDD和微服务架构简直就是天生一对。DDD的子域和限界上下文的概念,可以很好地跟微服务架构中的服务进行匹配。而且微服务架构中的自治化团队负责服务开发的概念,也跟DDD中每个领域模型都由一个独立团队负责开发的概念吻合。更有趣的是,子域用于它自己的领域模型这个概念,为消除上帝类和优化服务拆分提供了好办法。按子域分解和按业务能力分解是定义应用程序的微服务架构的两种主要模式。

2.4 拆分的指导原则

  • 单一职责原则
    类所承载的每一个职责都是对它进行修改的潜在原因。如果一个类承载了多个职责,并且互相之间的修改是独立的,那么这个类就会变得非常不稳定。遵照SRP原则,你所定义的每一个类都应该只有一个职责,因此也就只有一个理由对它进行修改。
    我们在设计微服务架构时应该遵循SRP原则,设计小的、内聚的、仅仅含有单一职责
    的服务。这会缩小服务的大小并提升它的稳定性。
  • 闭包原则
    如果由于某些原因,两个类的修改必须耦合先后发生,那么就应该把它们放在同一个包内。这样做的目标是当业务规则发生变化时,开发者只需要对一个交付包做出修改,而不是大规模地修改(和重新编译)整个应用。采用闭包原则,极大地改善了应用程序的可维护性。在微服务架构下,我们就能把根据同样原因进行变化的服务放在个组件内。这样做可以控制服务的数量,当需求发生变化时,变更和部署也更加容易。

2.5 拆分单体应用为服务的难点

通过定义与业务能力或子域相对应的服务来创建微服务架构的策略看起来很简单。但是,你可能会遇到几个障碍:

  • 网络延迟。
  • 同步进程间通信导致可用性降低。
  • 在服务之间维持数据一致性。
  • 获取一致的数据视图。
  • 上帝类阻碍了拆分。

2.6 定义服务API

到目前为止,我们有一个系统操作列表和一个潜在服务列表。下一步是定义每个服务的API:也就是服务的操作和事件。存在服务API操作有以下两个原因:首先,某些操作对应于系统操作。它们由外部客户端调用,也可能由其他服务调用。另次,存在一些其他操作用以支持服务之间的协作。这些操作仅由其他服务调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值