《微服务架构设计模式》读书笔记 | 第5章 微服务架构中的业务逻辑设计

本文主要是介绍《微服务架构设计模式》读书笔记 | 第5章 微服务架构中的业务逻辑设计,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第5章 微服务架构中的业务逻辑设计

  • 前言
  • 1. 业务逻辑组织模式
    • 1.1 一个典型的服务架构
    • 1.2 使用事务脚本模式设计业务逻辑
    • 1.3 使用领域模型模式设计业务逻辑
    • 1.4 关于领域驱动设计
  • 2. 使用聚合模式设计领域模型
    • 2.1 聚合拥有明确的边界
    • 2.2 聚合规则
    • 2.3 聚合的颗粒度
    • 2.4 使用聚合设计业务
    • 2.5 Order Service基于聚合设计的业务逻辑
  • 3. 发布领域事件
    • 3.1 领域事件的应用场景
    • 3.2 领域事件的特点
    • 3.3 事件增强
    • 3.4 识别领域事件
    • 3.5 生成领域事件
    • 3.6 发布领域事件
    • 3.7 消费领域事件
  • 4. Kichen Service的业务逻辑
    • 4.1 Kichen Service的设计
    • 4.2 Ticket类的结构
    • 4.3 Ticket聚合的行为
    • 4.4 KitchenService的领域服务
    • 4.5 KitchenServiceCommandHandler类
  • 5. Order Service的业务逻辑
    • 5.1 Order Service的设计
    • 5.2 Order聚合的结构
    • 5.3 Order聚合状态机
    • 5.4 OrderService类
  • 6. 微服务与单体应用程序的业务逻辑异同点
  • 7. 本章小结
  • 最后


前言

这是一本关于微服务架构设计方面的书,这是本人阅读的学习笔记。首先对一些符号做些说明:

()为补充,一般是书本里的内容;
[]符号为笔者笔注;


1. 业务逻辑组织模式

组织业务逻辑有两种主要的模式:面向过程的事务脚本模式和面向对象的领域建模模式。

1.1 一个典型的服务架构

业务逻辑周围是入站和出站适配器。

  • 入站适配器:处理来自客户端的请求并调用业务逻辑;
  • 出站适配器:被业务逻辑调用,然后它们再调用其他服务和外部程序应用;

Order Service具有六边形架构

Order Service具有六边形架构
此服务由业务逻辑和以下适配器组成:

  • REST API adapter:入站适配器,实现REST API,这些API会调用业务逻辑;
  • OrderCommandHandlers:入站适配器,它接受来自消息代理的出站适配器,并调用业务逻辑;
  • Database Adapter:由业务逻辑调用以访问数据库的出站适配器;
  • Domain Event Publishing Adapter:将事件发布到消息代理的出站适配器;

1.2 使用事务脚本模式设计业务逻辑

事务脚本:将业务逻辑组织为面向过程的事务脚本的集合,每种类型的请求都有一个脚本。

使用事务脚本模式设计业务逻辑
特点

  • 实现行为的类与存储状态的类是分开的;
  • 脚本通常在服务类中;
  • 每个服务都有一个用于请求或系统操作的方法;这个方法实现请求的业务逻辑;

1.3 使用领域模型模式设计业务逻辑

领域模型:将业务逻辑组织为由具有状态和行为的类构成的对象模型。

使用领域模型模式设计业务逻辑
特点

  • 服务方法通常很简单(因为服务方法几乎总是调用持久化领域对象,这些对象中包含大量业务逻辑);
    • 如:Order类具有状态和方法,状态是私有的,只能通过它的方法间接访问;
  • 易于理解、维护、测试和扩展;

1.4 关于领域驱动设计

领域驱动设计(DDD)是对面向对象设计的改进,是开发复杂业务逻辑的一种方法。其基本元素如下:

  • 实体(entity):具有持久化ID的对象。具有相同属性值的两个实体仍然是不同的对象;
  • 值对象(value object):作为值集合的对象。具有相同属性值的两个值对象可以互换使用;
  • 工厂(factory):负责实现对象创建逻辑的对象或方法,该逻辑过于复杂,无法由类的构造函数直接完成。它还可以隐藏被实例化的具体类。工厂方法一般可实现为类的静态方法;
  • 存储库(repository):用来访问持久化实体的对象,储存库也封装了访问数据库的底层机制;
  • 服务(service):实现不属于实体或值对象的业务逻辑对象;

2. 使用聚合模式设计领域模型

传统领域模型缺少每个业务对象的明确边界,DDD聚合旨在解决此问题。

2.1 聚合拥有明确的边界

聚合模式:将领域模型组织为聚合的集合,每个聚合都是可以作为一个单元进行处理的一组对象构成的图。

Order 聚合及其边界

Order 聚合及其边界

  • 聚合代表了一致的边界
    • 更新整个聚合可以解决一致性问题;
  • 识别聚合是关键
    • 在驱动领域设计中,设计领域模型的关键部分是识别聚合,以及它们的边界和根;

2.2 聚合规则

聚合规则可以确保聚合是一个可以强制执行各种不变量约束的自包含单元。

  • 规则一:只引用聚合根
    • 聚合根是聚合中唯一可以由外部类引用的部分;客户端只能通过调用聚合根上的方法来更新聚合;
    • 如:服务使用储存库从数据库加载聚合并获取聚合根的引用;
  • 规则二:聚合间的引用必须使用主键
    • 如:Order使用consumerId引用其Consumer而不是直接引用Consumer对象;
  • 规则三:在一个事务中,只能创建或更新一个聚合
    • 这个约束可以确保单个事务的范围不超越服务的边界;还满足大多数NoSQL数据库的受限事务模型;
    • 这个规则让创建或更新多个聚合的操作变得更加复杂,但可以通过Saga解决;

事务只能创建或更新单个聚合


2.3 聚合的颗粒度

  • 由于每个聚合的更新都是序列化的,因此更细颗粒度的聚合间提高应用程序能同时处理的请求数量,从而提高可扩展性;
  • 另一方面,因为聚合是事务的范围,所以可能需要定义更大的聚合以使特定的聚合更新操作满足事务的原子性;
  • 因此,在开发领域模型时,必须做出的关键决策是决定每个聚合的大小;

2.4 使用聚合设计业务

  • 在典型的微服务中,大部分业务逻辑由聚合组成;其余业务逻辑存在与领域服务和Saga中;
  • Saga编排本地事务的序列,以确保数据的一致性;
  • 服务是业务逻辑的入口,由入站适配器调用;
  • 服务使用存储库从数据库中检索聚合或将聚合保存到数据库;
  • 每个存储库都由访问数据库的出站适配器实现;

2.5 Order Service基于聚合设计的业务逻辑

Order Service基于聚合设计的业务逻辑

  • 业务逻辑由Order聚合、OrderService服务类、OrderRepository和一个或多个Saga组成;
  • OrderService调用OrderRepository来保存和加载Order;
  • 对于能在服务内部完成处理的简单请求,服务直接更新Order聚合;
  • 如果更新请求跨越多个服务,OrderService将创建一个Saga;

3. 发布领域事件

领域事件:聚合在被创建时,或发生其他重大更改时发布领域事件。

3.1 领域事件的应用场景

  • 使用基于编排的Saga维护服务之间的数据一致性【第四章】;
  • 通知维护数据副本的服务,源数据已经发生了更改;这种方法称为命令查询职责隔离(CQRS)【第七章】;
  • 通过Webhook或消息代理通知不同的应用程序,以触发下一个业务流程;
  • 按顺序通知同一应用程序的不同组件;
  • 向用户发送短信或电子邮件通知,告诉他们订单发货、航班延误等消息;
  • 监控领域事件以验证应用程序是否正常运行;
  • 分析领域事件,为用户行为建模;

3.2 领域事件的特点

  • 命名领域事件时,往往选择动词的过去分词;
  • 领域事件的每个属性都是原始值或值对象;
    • 如:OrderCreated事件类具有orderId属性;
  • 领域事件通常具有元数据,如事件ID和时间戳;
  • 元数据可以是事件对象的一部分,可能在超类中定义;

OrderCreated事件是领域事件的一个例子

  • DomainEvent接口是一个标识接口,用于将类标识为领域事件;
  • OrderDomainEvent是Order聚合发布的事件的标识接口(如OrderCreated);
  • DomainEventEnvelope是一个包含事件元数据和事件对象的类;

3.3 事件增强

当OrderCreated的事件接收方需要订单的详细信息时,一种办法是从Order Service中检索该信息,让事件接收方查询聚合服务,缺点是会产生服务请求的开销;

另一种方案是事件增强:

  • 事件包含接收方需要的信息
  • 缺点:可能会使领域事件的稳定性降低;每当接收方的需求发生改变时,事件类都可能需要更改;可能会降低可维护性;

在这里插入图片描述

3.4 识别领域事件

可以使用事件风暴方法,其结果是一个以事件为中心的领域模型,它由聚合和事件组成;包括以下三个步骤:

  1. 头脑风暴:请求领域专家集体讨论领域事件;
  2. 识别事件触发器:请求领域专家确定每个事件的触发器(如:用户操作、外部系统、另一个领域事件、时间的流逝等);
  3. 识别聚合:请求领域专家识别那些使用命令的聚合并发出相应的事件;

3.5 生成领域事件

在聚合和调用它的服务(或类)之间分配职责。

  • 服务可以使用依赖注入来获取对消息传递API的引用,从而轻松发布事件;
  • 只要状态发生变化,聚合就会生成事件并将它们返回给服务;
  • 聚合可以通过以下方法将事件返回给服务:

在聚合方法的返回值中包括一个事件列表


该服务调用聚合根方法,然后发布事件;


聚合根在一个内部字段中累积保存事件:然后服务检索这些事件并发布它们;

3.6 发布领域事件

服务必须使用事务性消息来发布事件,以确保领域事件是作为更新数据库中聚合的事务的一部分对外发布;

Eventuate Tram框架提供DomainEventPublisher接口

DomainEventPublisher接口
让服务实现AbstractAggregateDomainEventPublisher的子类:它为发布领域事件提供了类型安全的接口;

AbstractAggregateDomainEventPublishe接口

实现AbstractAggregateDomainEventPublisher子类

3.7 消费领域事件

领域事件是接收方使用更高级的API,如:Eventuate Tram框架的DomainEventDispatcher等。其可以将领域事件调度到适当的处理程序方法。

  • 每当餐馆的菜单更新时,KitchenServiceEventConsumer都会订阅Restaurant Service发布事件;
  • 它负责使Kitchen Service的数据副本保持最新;

4. Kichen Service的业务逻辑

该服务的主要功能是负责实现餐馆的订单管理功能。其两个主要聚合是Restaurant和Ticket;

4.1 Kichen Service的设计

Kitchen Service的设计

  • 两个聚合:
    • Restaurant:知道餐馆的菜单和营业事件,并可以验证订单;
    • Ticket:工单烹饪完成后由送餐员负责派送;
  • 核心业务:
    • KitchenService:业务入口,定义了创建和更新Restaurant及Ticket聚合的方法;
    • TicketRepository:定义了持久化Tickets的方法;
    • RestaurantRepository:定义了持久化Restaurants的方法;
  • 三个入站适配器:
    • REST API:餐馆工作人员通过他们的用户界面调用这些REST API;
    • KitchenServiceCommandHandler:由Saga调用的基于异步请求 / 响应的API;它调用KitchenService来创建和更新Ticket;
    • KitchenServiceEventConsumer:订阅Restaurant Service发布的事件;它调用KitchenService来创建和更新Restaurant聚合;
  • 两个出站适配器:
    • DB Adapter:实现TicketRepository和RestaurantRepository接口并访问数据库;
    • DomainEventPublishingAdapter:实现DomainEventPublisher接口并发布Ticket领域事件;

4.2 Ticket类的结构

该类使用JPA进行持久化,并映射到TICKETS表。


Ticket类的结构

4.3 Ticket聚合的行为


Ticket的一些方法

  • create():创建Ticket的工厂方法;
  • accept():餐馆已接收订单;
  • preparing():餐馆已开始准备订单,意味着订单无法再更改或取消;
  • readyForPickup():订单可以派送;

4.4 KitchenService的领域服务

KitchenService由服务入站适配器调用;定义了用于更改订单状态的各种方法(如accept()、reject()、preparing()等);每个方法加载指定的聚合,在聚合根上调用相应的方法,并发布领域事件;如下accept()方法所示:

accept()方法更新Ticket
accept()方法的两个参数

  • orderId:要接受订单的ID;
  • readyBy:订单可被派送的预计时间;

4.5 KitchenServiceCommandHandler类

KitchenServiceCommandHandler类是一个适配器,负责处理Order Service实现的各种Saga发送的命令式消息;

KitchenServiceCommandHandler类


5. Order Service的业务逻辑

5.1 Order Service的设计

Order Service的设计

  • 几个入站适配器:
    • REST API:供消费者利用用户界面调用的REST API;它调用OrderService来创建和更新Order;
    • OrderEventConsumer:订阅Restaurant Service发布的活动;它调用OrderService来创建和更新其Restaurant副本;
    • OrderCommandHandlers:由Saga调用的基于异步请求 / 相应的API;它调用OrderService来更新Order;
    • SagaReplyAdapter:订阅Saga回复通道并调用Saga;
  • 一些出站适配器:
    • DB Adapter:实现OrderRepository接口并访问Order Service的数据库;
    • DomainEventPublishingAdapter:实现DomainEventPublisher接口并发布Order领域事件;
    • OutboundCommandMessageAdapter:实现CommandPublisher接口并向Saga参与方发送命令式消息;

5.2 Order聚合的结构

Order类是Order聚合的根;Order聚合还包括了值对象,如:OrderLineItem、DeliveryInfo和PaymentInfo。

Order聚合的设计
Order类和它的字段

此类使用JPA持久化,并映射到ORDERS表。


Order类和它的字段

5.3 Order聚合状态机

为了创建或更新订单,Order Service必须使用Saga与其他服务协作。

Order聚合状态机
创建Order过程中调用的Order方法

创建Order过程中调用的方法

更新Order需要调用的方法


更新Order需要调用的方法

5.4 OrderService类

该类定义了用于创建和更新Orders的方法。

OrderService类


6. 微服务与单体应用程序的业务逻辑异同点

  • 相同点
    • 由诸如服务、JPA支持的实体类和存储库等这样的类组成;
  • 不同点
    • 领域模型被组织为一组DDD聚合,在其上可以施加各种约束;
    • 与传统的对象模型不同,不同聚合中的类之间的引用是基于主键而不是对象引用;
    • 事务只能创建或更新单个聚合;聚合在状态发生变化时会发布领域事件;
    • 服务通常使用Saga来维护多个服务之间的数据一致性;

7. 本章小结

  • 事务脚本模式通常是实现简单业务的好办法。但在实现复杂的业务逻辑时,应该考虑使用面向对象的领域模型模式;
  • 设计服务的业务逻辑的好办法是使用DDD聚合。DDD聚合很有用,因为它们把领域模型模块化,消除了服务之间对象的直接引用,并确保每个ACID事务都在服务内;
  • 创建或更新聚合时应发布领域事件。领域事件具有广泛的用途。第4章讨论了如何实现协同式Saga。第7章中将讨论如何使用领域事件来更新从其他服务复制来的数据。领域事件的订阅者还可以通知用户和其他应用程序,并将WebSocket消息发布到用户的浏览器。

最后

新人制作,如有错误,欢迎指出,感激不尽!
欢迎关注公众号,会分享一些更日常的东西!
如需转载,请标注出处!

这篇关于《微服务架构设计模式》读书笔记 | 第5章 微服务架构中的业务逻辑设计的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/385660

相关文章

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Java异常架构Exception(异常)详解

《Java异常架构Exception(异常)详解》:本文主要介绍Java异常架构Exception(异常),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. Exception 类的概述Exception的分类2. 受检异常(Checked Exception)

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

Linux上设置Ollama服务配置(常用环境变量)

《Linux上设置Ollama服务配置(常用环境变量)》本文主要介绍了Linux上设置Ollama服务配置(常用环境变量),Ollama提供了多种环境变量供配置,如调试模式、模型目录等,下面就来介绍一... 目录在 linux 上设置环境变量配置 OllamPOgxSRJfa手动安装安装特定版本查看日志在

SpringCloud之LoadBalancer负载均衡服务调用过程

《SpringCloud之LoadBalancer负载均衡服务调用过程》:本文主要介绍SpringCloud之LoadBalancer负载均衡服务调用过程,具有很好的参考价值,希望对大家有所帮助,... 目录前言一、LoadBalancer是什么?二、使用步骤1、启动consul2、客户端加入依赖3、以服务

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架

Nginx配置系统服务&设置环境变量方式

《Nginx配置系统服务&设置环境变量方式》本文介绍了如何将Nginx配置为系统服务并设置环境变量,以便更方便地对Nginx进行操作,通过配置系统服务,可以使用系统命令来启动、停止或重新加载Nginx... 目录1.Nginx操作问题2.配置系统服android务3.设置环境变量总结1.Nginx操作问题

springboot的调度服务与异步服务使用详解

《springboot的调度服务与异步服务使用详解》本文主要介绍了Java的ScheduledExecutorService接口和SpringBoot中如何使用调度线程池,包括核心参数、创建方式、自定... 目录1.调度服务1.1.JDK之ScheduledExecutorService1.2.spring

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后