微服务设计模式-服务分解策略
软件的架构可以通俗理解为:一个应用如何拆解为多个组成部分,和这些组成部分之间的关系。
什么是微服务
分层架构
分层架构是一种精典的架构体系,一个系统可以被分为多层,每一层去承担一个责任。分层架构限定了层级之间的依赖,每层必须依赖于它的 直接下层或者间接下层。 有名的三层架构将一个系统分为三层,
- 展现层-包含了实现用户接口和外部API的代码。
- 业务逻辑层-包含了业务逻辑。
- 持久层-实现了和数据库交互的业务逻辑。 分层架构是一个种很好的架构风格,但是也有一些明显的缺点。
- 单一的展现层-不能体现出应用被不止一个系统调用。
- 单一的持久层-不能体现出应用有可能和多个数据库有交互。
- 基于持久层定义业务逻辑层-也就是说,没有数据库,你就不能测试业务逻辑。
六边形架构
六边形架构可以用于替代分层架构。如下图所示,六边形架构将业务逻辑层放到中心位置,相比于封层架构的展现层,六边形架构有一到多个 入口适配器(如REST,web pages等),通过调用业务逻辑来处理从外部进入的请求。应用有也有一到多个外部适配器(DAO等),调用业务 逻辑和外部进行交互。
业务逻辑拥有多个端口(紫色),在java中,一个端口通常是一个接口,这些端口被分为两类:入口端口和出口端口,入口端口时业务逻辑 提供给外部应用调用的,如一个service接口,多个入口适配器可以调用同一个入口端口。出口端口用于调用外部应用,如一个仓库接口, 定义了一系列数据访问操作,用于调用外部数据库。
该架构的一个主要好处是将业务逻辑层通过适配器解耦,业务逻辑不会依赖展现层逻辑和数据访问层逻辑。由于解耦,很容易单独测试业务 逻辑层。
微服务架构
微服务是一种架构风格,它由多个独立的服务组成,每个服务类似于六边形架构。服务之间充分实现解耦,通过REST或异步消息等进行通信。 下面是外卖应用FTGO扩展为微服务后大概的应用架构图:
- Order Service:管理订单服务。
- Delivery Service:管理订单的派送
- Restaurant Service:管理餐厅的信息
- Kitchen Service:管理订单的准备。
- Accounting Service:管理账单和支付。
定义一个微服务的架构
定义一个系统的架构不是按部就班的过程,而是一种艺术,需要不断的迭代和创新。下面是一个构件微服务的案例,可以作为一个参考, 大概分为三步,如下图。
- 从需求出发,如用户故事,定义系统的操作,系统操作是整个应用必须处理的请求的抽象,是使用抽象领域模型从需求中定义出来的。
- 定义系统有哪些服务,分解服务有两种策略,一种是根据业务能力定义,另一种使用领域驱动模型设计子模型划分。
- 定义服务API和服务之间的交互协作,定义每个服务的API,将第一步每个系统操作分配给每个服务。一个系统操作可能需要多个service 协同完成。
定义系统操作
定义系统操作需要从需求出发,包括用户案例和对应的用户场景。系统操作的标识分为两步处理。第一步,创建一个由关键类组成的高层次 领域模型。第二步,定义系统操作,并使用领域模型描述每个系统操作的行为。
一个领域模型通过诸如故事和场景名词分析等即使来创建。如产生订单故事:
有一个消费者
一个餐馆
一个运送地址/时间。
一个满足餐馆最低订单标准的订单。
当消费者在餐馆产生一个订单
消费者的账户被授权
创建等待接单状态下的订单。
订单关联到消费者
订单关联到餐厅
该用户场景下,包含的多个类,如:消费者(Consumer),订单(Order),餐厅(Restaurant),和账户(Account)。
类似的,订单接收也可以扩充为如下的场景:
有一个等待接单状态的订单
和一个可以送单的快递员
当餐馆接单后,并承诺送达时间,
接着,订单被置为已接单状态
设置订单的送达时间
将此单分配给快递员
以上场景反应出快递员(Courier)和快递(Delivery)两个类,经过类似的一些回归分析,我们可以得到其他如MenuItem、Address类。 如下类图,显示出主要的类:
上面的类以及各自的职责如下:
- Consumer:产生订单的用户。
- Order:由消费者产生,描述订单和订单状态。
- OrderLineItem:订单中的一条。
- DeliveryInfo:交付一个订单的地址和时间。
- Restaurant:餐厅用于准备订单。
- MenuItem:餐厅菜单上的一项。
- Courier:快递员。
- Address:用于记录消费者或者餐馆的地址。
- Location:快递员的经纬度。
上面我们已经建立起一个领域模型,那么接下来,我们定义应用必须处理的请求。系统 操作可以简单地分为两种:
- 命令-创建、更新、删除数据相关的系统操作。
- 查询-读取数据相关的系统操作。
一个系统命令主要从用户故事中获取,主要包含下面几个元素:
每个命令有它的参数、返回值、前置条件和后置条件。如创建订单的操作如下:
如下是接单的系统操作说明:
定义服务
一旦系统操作定义完成,下一步就是定义应用的服务,可以使用不同的分解策略,但是所有的分解策略,分解结果却是相同的,那么就是 一个架构的服务组成,主要是基于业务构建的,而不是基于技术概念。
根据业务能力分解应用
构建微服务的一种策略就是基于业务能力。一个组织的业务能力通过分析组织的目的,结构,还有业务过程来定义,每一个业务能力可以 想象成一个服务。 一个业务能力通常集中在一个特殊的业务对象上,如通知业务,聚焦于通知管理能力上。一个能力一般由多个子能力 组成。 通过分析我们可以得到FTGO外卖系统的业务能力如下:
- 供给管理
- 快递员管理-管理快递员信息。
- 餐馆信息管理-管理餐馆的订单,开放时间,位置等信息。
- 消费者管理-管理消费者信息。
- 订单生产和履约
- 订单管理:消费者创建和管理订单。
- 餐厅订单管理:一个餐馆内,管理订单的生产。
- 后勤
- 可用快递员管理—实时的管理可用的快递员,用以送订单。
- 运送管理-为消费者运送订单。
- 账户
- 消费者账户-管理消费者账单。
- 餐厅账户-管理餐厅的营收。
- 快递员账户-管理快递员的营收。
我们定义好了业务能力之后,就需要为每个业务能力或者一组业务能力定义一个服务了。 这种映射的依据根据经验和实际情况而定。如下就是我们将FTGO外卖系统的业务能力定义为对应的服务:
根据业务能力划分服务的一个好处是,由于业务能力一般比较稳定,那么对应的架构也会比较稳定,独立的组件服务可能会根据业务的改 变而变化,但是整个架构不会改变。
根据领域模式分解应用
DDD(Domain-Driven Design)是让复杂的软件应用建立面向对象的领域模型的一种有效方法。DDD有两个概念在微服务架构中非常有用: 子领域和边界上下文。
在传统的模型设计中,可能会对不同的概念使用相同的术语,或者相同的概念又使用了不同的术语,让人困惑。和传统的企业模型不同, DDD为每个子领域定义一个单独的域模型。一个子领域是整个领域的一部分,子领域的划分和划分业务能力的方式一样:分析业务,并 标识出不同区域。子领域划分的结果和业务能力划分的结果非常类似。如下图,FTGO外卖系统划分的结果如下:
DDD将一个领域模型的范围称为边界上下文。一个边界上下文包括了实现领域模型的代码组件。在微服务架构中,一个边界上下文是一个服务 或者一组服务。
分解的一些规则
单一责任原则
每个类只有一个改变它的因素。
要让一个类只有一个改变它的因素,那么一个类只承担一个责任,同时多个类也不能共享一个责任。如:订单类只能负责订单相关的 责任,而商品不能包含订单修改的责任。
共同封闭原则
如果两个类由于共同的原因同时发生改变,那么他们应该属于同一个包,即改变应尽可能地缩小影响的范围,提高应用的可维护性。
分解应用面临的阻碍
- 网络延迟,分解服务导致了服务之间的网络回路增加,可以通过批处理一次发送多个对象减少这种延迟。
- 同步通信的可靠性降低。服务之间的调用通过Rest或者RPC,服务出错导致通信失败。
- 跨服务维护数据一致性,分布式事务等。
- 数据孤立。
- God Class阻碍分解。
God Class通常在整个应用中使用,实现了应用多方面的业务逻辑。通常包含多个字段。是领域模型的中心,如:银行中的账户, 电商中的订单等等。由于它将应用多方面的状态和行为绑在一起,所以是分解业务逻辑到服务 中的不可逾越障碍。
FTGO外卖应用中最大的God Class是Order,下图显示了使用传统的模型技术时,Order的机构:
上图中,Order类包含了各个方面的字段和方法。
使用DDD,将每个服务作为一个独立的子领域,并拥有自己的域模型,意思是,FTGO应用中的每个与Order相关的服务都有其自己版本 的关于Order类的域模型。如Dilivery Service,它关于Order的视图如下:
使用DDD改造后的Order如下图:
定义服务API
在划分好服务后,我们需要将系统操作划分给服务,有时候,一个操作的划分不太明显,如;位置更新通知noteUpdatedLocation(), 用于更新快递员的位置。一方面该操作和Courier有关,所以应该划分到Courier Service,另一方面,它也是快递服务,应该划分为 Delivery Service。这种情况下,将它划分到需要操作提供信息的服务合适(Delivery 需要改操作的信息)。在其他情况下,应该划分 到有该操作相关信息的服务(Courier)。
下图是FTGO的一些系统操作的划分:
在划分好系统操作后,下一步需要定义service之间协作需要的API。因为一些操作可能需要跨服务完成交互。
下图是FTGO操作之间需要的跨服务协作。