05.领域驱动设计:认识领域事件,解耦微服务的关键

目录

1、概述

2、领域事件

2.1 如何识别领域事件

1.微服务内的领域事件

2.微服务之间的领域事件

3、领域事件总体架构

3.1 事件构建和发布

3.2 事件数据持久化

3.3 事件总线 (EventBus)

3.4 消息中间件

3.5 事件接收和处理

4、案例

5、总结


1、概述

在事件风暴(Event Storming)时,我们知道除了命令和操作等业务行为以外,还有一种非常重要

的事件,这种事件发生后通常会导致进一步的业务操作,在DDD中这种事件被称为领域事件

那到底什么是领域事件?领域事件的技术实现机制是怎样的?

2、领域事件

领域事件是领域模型中非常重要的一部分,用来表示领域中发生的事件。一个领域事件将导

致进一步的业务操作,在实现业务解耦的同时,还有助于形成完整的业务闭环

举例来说的话,领域事件可以是业务流程的一个步骤,比如投保业务缴费完成后,触发投保

单转保单的动作;也可能是定时批处理过程中发生的事件,比如批处理生成季缴保费通知

单,触发发送缴费邮件通知操作;或者一个事件发生后触发的后续动作,比如密码连续输错

三次,触发锁定账户的动作。

2.1 如何识别领域事件

在做用户旅程或者场景分析时,我们要捕捉业务、需求人员或领域专家口中的关键词:“如果发

生……,则……”“当做完……的时候,请通知……”“发生……时,则……”等。

在这些场景中,如果发生某种事件后,会触发进一步的操作,那么这个事件很可能就是领域事件

领域事件为什么要用最终一致性,而不是传统SOA的直接调用的方式呢?

聚合的一个设计原则:在边界之外使用最终一致性

一次事务最多只能更改一个聚合的状态。如果一次业务操作涉及多个聚合状态的更改,应采用领域

事件的最终一致性。

领域事件驱动设计可以切断领域模型之间的强依赖关系,事件发布完成后,发布方不必关心后续订

阅方事件处理是否成功,这样可以实现领域模型的解耦,维护领域模型的独立性和数据的一致性。

在领域模型映射到微服务系统架构时,领域事件可以解耦微服务,微服务之间的数据不必要求强一

致性,而是基于事件的最终一致性。

1.微服务内的领域事件

当领域事件发生在微服务内的聚合之间,领域事件发生后完成事件实体构建和事件数据持久化,

布方聚合将事件发布到事件总线,订阅方接收事件数据完成后续业务操作。

微服务内大部分事件的集成,都发生在同一个进程内,进程自身可以很好地控制事务,因此不一定

需要引入消息中间件。但一个事件如果同时更新多个聚合,按照DDD“一次事务只更新一个聚合”的

原则,你就要考虑是否引入事件总线。

微服务内应用服务,可以通过跨聚合的服务编排和组合,以服务调用的方式完成跨聚合的访问,这

种方式通常应用于实时性和数据一致性要求高的场景。这个过程会用到分布式事务,以保证发布方

和订阅方的数据同时更新成功。

2.微服务之间的领域事件

跨微服务的领域事件会在不同的限界上下文或领域模型之间实现业务协作,其主要目的是实现微服

务解耦,减轻微服务之间实时服务访问的压力。

跨微服务的事件机制要总体考虑事件构建、发布和订阅、事件数据持久化、消息中间件,甚至事件

数据持久化时还可能需要考虑引入分布式事务机制等。

微服务之间的访问也可以采用应用服务直接调用的方式,实现数据和服务的实时访问,弊端就是跨

微服务的数据同时变更需要引入分布式事务,以确保数据的一致性。分布式事务机制会影响系统性

能,增加微服务之间的耦合,所以我们还是要尽量避免使用分布式事务

总之,通过领域事件驱动的异步化机制,可以推动业务流程和数据在各个不同微服务之间的流转,

实现微服务的解耦,减轻微服务之间服务调用的压力,提升用户体验。

3、领域事件总体架构

领域事件的执行,需要一系列的组件和技术来支撑。领域事件处理包括:事件构建和发布、事件数

据持久化、事件总线、消息中间件、事件接收和处理等。

3.1 事件构建和发布

事件基本属性至少包括:事件唯一标识、发生时间、事件类型和事件源,其中事件唯一标识应该是

全局唯一的,以便事件能够无歧义地在多个限界上下文中传递。事件基本属性主要记录事件自身以

及事件发生背景的数据

另外事件中还有一项更重要,那就是业务属性,用于记录事件发生那一刻的业务数据,这些数据会

随事件传输到订阅方,以开展下一步的业务操作

事件基本属性和业务属性一起构成事件实体,事件实体依赖聚合根。领域事件发生后,事件中的业

务数据不再修改,因此业务数据可以以序列化值对象的形式保存,这种存储格式在消息中间件中也

比较容易解析和获取。

为了保证事件结构的统一,我们还会创建事件基类DomainEvent,子类可以扩充属性和方法。由

于事件没有太多的业务行为,实现方法一般比较简单。

事件发布之前需要先构建事件实体并持久化。事件发布的方式有很多种,你可以通过应用服务或者

领域服务发布到事件总线或者消息中间件,也可以从事件表中利用定时程序数据库日志捕获技术

获取增量事件数据,发布到消息中间件。

3.2 事件数据持久化

事件数据持久化可用于系统之间的数据对账,或者实现发布方和订阅方事件数据的审计。当遇到消

息中间件、订阅方系统宕机或者网络中断,在问题解决后仍可继续后续业务流转,保证数据的一致

性。

事件数据持久化有两种方案:

  • 持久化到本地业务数据库的事件表中,利用本地事务保证业务和事件数据的一致性。

  • 持久化到共享的事件数据库中

需要注意的是:业务数据库和事件数据库不在一个数据库中,它们的数据持久化操作会跨数据库,

因此需要分布式事务机制来保证业务和事件数据的强一致性,结果就是会对系统性能造成一定的影

3.3 事件总线 (EventBus)

事件总线是实现微服务内聚合之间领域事件的重要组件,它提供事件分发和接收等服务。

事件总线是进程内模型,它会在微服务内聚合之间遍历订阅者列表,采取同步或异步的模式传递数据。

事件分发流程大致如下:

  • 如果是微服务内的订阅者(其它聚合),则直接分发到指定订阅者;

  • 如果是微服务外的订阅者,将事件数据保存到事件库(表)并异步发送到消息中间件;

  • 如果同时存在微服务内和外订阅者,则先分发到内部订阅者,将事件消息保存到事件库(表),再异步发送到消息中间件。

3.4 消息中间件

跨微服务的领域事件大多会用到消息中间件,实现跨微服务的事件发布和订阅

消息中间件的产品非常成熟,市场上可选的技术也非常多,比如:Kafka,RabbitMQ等。

3.5 事件接收和处理

微服务订阅方在应用层采用监听机制,接收消息队列中的事件数据,完成事件数据的持久化后,就

可以开始进一步的业务处理。领域事件处理可在领域服务中实现。

4、案例

发生的领域事件是:缴费通知单已生成。下一步的业务操作是:缴费。

事件起点:出单员生成投保单,核保通过后,发起生成缴费通知单的操作。

  1. 投保微服务应用服务,调用聚合中的领域服务createPaymentNotice 和createPaymentNoticeEvent,分别创建缴费通知单、缴费通知单事件。其中缴费通知单事件类PaymentNoticeEvent继承基类DomainEvent。

  2. 利用仓储服务持久化缴费通知单相关的业务和事件数据。为了避免分布式事务,这些业务和事件数据都持久化。

  3. 到本地投保微服务数据库中。

  4. 通过数据库日志捕获技术或者定时程序,从数据库事件表中获取事件增量数据,发布到消息中间件。这里说明:事件发布也可以通过应用服务或者领域服务完成发布。

  5. 收款微服务在应用层从消息中间件订阅缴费通知单事件消息主题,监听并获取事件数据后,应用服务调用领域层的领域服务将事件数据持久化到本地数据库中。

  6. 收款微服务调用领域层的领域服务PayPremium,完成缴费。

  7. 事件结束。

提示:缴费完成后,后续流程的微服务还会产生很多新的领域事件,比如缴费已完成、保单已保存

等等。这些后续的事件处理基本上跟1~6的处理机制类似。

5、总结

今天我们主要学习领域事件以及领域事件的处理机制

领域事件驱动是很成熟的技术,在很多分布式架构中得到了大量的使用。领域事件是DDD的一个

重要概念,在设计时我们要重点关注领域事件,用领域事件来驱动业务的流转,尽量采用基于事件

的最终一致,降低微服务之间直接访问的压力,实现微服务之间的解耦,维护领域模型的独立性和

数据一致性。

除此之外,领域事件驱动机制可以实现一个发布方N个订阅方的模式,这在传统的直接服务调用设

计中基本是不可能做到的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/247702.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

容器和虚拟机的对比

容器和虚拟机的对比 容器和虚拟机在与硬件和底层操作系统交互的方式上有所不同 虚拟化 使多个操作系统能够同时在一个硬件平台上运行。 使用虚拟机监控程序将硬件分为多个虚拟硬件系统,从而允许多个操作系统并行运行。 需要一个完整的操作系统环境来支持该应用。…

Go语言grpc服务开发——Protocol Buffer

文章目录 一、Protocol Buffer简介二、Protocol Buffer编译器安装三、proto3语言指南四、序列化与反序列化五、引入grpc-gateway1、插件安装2、定义proto文件3、生成go文件4、实现Service服务5、gRPC服务启动方法6、gateway服务启动方法7、main函数启动8、验证 相关参考链接&am…

图片的安全防护方法

目录 一:图片加水印 二:访问地址权限控制 三:限制下载 四:CDN加速 一:图片加水印 1:在添加水印之前,要对图片进行必要的处理。例如调整亮度、对比度和锐度等,以提高水印的清晰度…

搜维尔科技:【简报】元宇宙数字人赛道,优秀作品《星云时报》赏析

AI 对人们来说是一种新产业,而人们对于它未来会面临的议题仍有许多疑虑,因此我们用新闻报导的方式列举一些有趣且具有可能性的标题,希望能让 大家了解 AI 在未来可能会带来什么问题,以及我们应该采取的态度。 学校: 新…

web前端之不一样的居中方式、解决tabBar选项卡居中问题、css支持嵌套、auto

MENU 前言htmlstyle效果 前言 这里不能使用justify-content: center;&#xff0c;因为在小屏幕上&#xff0c;这种方式无法显示最前面的两个tabBar。 html <div id"box" class"d_f o_a mt_50 mb_50 ml_20 mr_20"><div class"ws_n">…

SQL注入-sqli-labs-master第一关

实验环境&#xff1a; Nginx.1.15.11 MySQL&#xff1a;5.7.26 实验步骤&#xff1a; 1.第一步&#xff1a; 在id1后加入一个闭合符号&#xff0c;如果报错&#xff0c;再在后面加上 -- 将后面注释掉&#xff0c;如果不报错&#xff0c;则证明为字符型。 http://127.0.0.1/…

Apache 辅助系统工具

一丶Apache Sqoop 1.Sqoop的介绍&#xff1a; Sqoop的工作机制是将导入或者导出的命令翻译成MapReduce实现&#xff0c;Sqoop可以理解为&#xff1a;SQL到Hadoop或者Hadoop到SQL 2.Sqoop的安装 配置文件修改&#xff1a; cd $SQOOP_HOME/conf mv sqoop-env-template.sh sqo…

###C语言程序设计-----C语言学习(6)#

前言&#xff1a;感谢老铁的浏览&#xff0c;希望老铁可以一键三连加个关注&#xff0c;您的支持和鼓励是我前进的动力&#xff0c;后续会分享更多学习编程的内容。 一. 主干知识的学习 1. while语句 除了for语句以外&#xff0c;while语句也用于实现循环&#xff0c;而且它…

Linux进程管理

什么是进程 进程与pid的关系 在程序被执行前&#xff0c;他们不过是硬盘或者其他存储介质中的一个文件。一旦被加载到内存中&#xff0c;程序会根据执行者的权限属性生成一串程序执行参数&#xff0c;并生成一个进程号&#xff0c;即PID。后续我们就可以根据这个PID完成对进程…

C/C++ - 内存管理(C++)

堆栈 C中的栈和堆是用于存储变量和对象​​的两个主要内存区域。栈是一种自动分配和释放内存的区域&#xff0c;用于存储局部变量和函数调用的上下文。栈上的内存分配和释放是自动进行的&#xff0c;无需手动管理。堆是动态分配内存的区域&#xff0c;用于存储动态创建的对象和…

滴滴基于 Ray 的 XGBoost 大规模分布式训练实践

背景介绍 作为机器学习模型的核心代表&#xff0c;XGBoost 在滴滴众多策略算法业务场景中发挥着至关重要的作用。因此&#xff0c;保障并持续提升 XGBoost 模型的离线训练及在线推理稳定性一直是机器学习平台的重点工作。同时&#xff0c;面对多样化的业务场景定制需求和数据规…

go语言(十八)---- goroutine

一、goroutine package mainimport ("fmt""time" )func main() {//用go创建承载一个形参为空&#xff0c;返回值为空的一个函数go func() {defer fmt.Println("A.defer")func() {defer fmt.Println("B.defer")//退出当前goroutinefmt…

Markdown(2篇文章学会Markdown

目录 1.文章链接&#xff1a;2.markdown可以用来解决什么问题&#xff1a;小结&#xff1a; 1.文章链接&#xff1a; Markdown&#xff08;2篇文章学会Markdown第一篇 Markdown&#xff08;2篇文章学会Markdown第二篇 2.markdown可以用来解决什么问题&#xff1a; 格式化文…

C++多线程1(复习向笔记)

创建线程以及相关函数 当用thread类创建线程对象绑定函数后&#xff0c;该线程在主线程执行时就已经自动开始执行了,join起到阻塞主线程的作用 #include <iostream> #include <thread> #include <string> using namespace std; //测试函数 void printStrin…

神经网络建立(结果可变)最小神经元

目录 介绍&#xff1a; 初始化&#xff1a; 建模: 预测&#xff1a; 改变结果&#xff1a; 介绍&#xff1a; 在深度学习中&#xff0c;神经元通常指的是人工神经元&#xff08;或感知器&#xff09;&#xff0c;它是深度神经网络中的基本单元。深度学习的神经元模拟了生…

【服务器GPT+MJ+GPTs】创建部署GPT+MJ+GPTs程序网站

目录 🌺【前言】 🌺【准备】 🌺【宝塔搭建GPT+MJ+GPTs】 🌼1. 给服务器添加端口 🌼2. 安装宝塔 🌼3. 安装Docker 🌼4. 安装ChatGPT程序 🌼5. 程序更新 🌼6. 修改端口 | 密码 🌼7. 绑定域名+申请SSL证书 🌺【前言】 相信大家都对openai的产品ch…

Kafka(九)跨集群数据镜像

目录 1 跨集群镜像的应用场景1.1 区域集群和中心集群1.2 高可用(HA)和灾备(DR)1.3 监管与合规1.4 云迁移1.5 聚合边缘集群的数据 2 多集群架构2.1 星型架构2.2 双活架构2.2 主备架构2.2.1 如何实现Kafka集群的故障转移2.2.1.1 故障转移包括的内容1. 灾难恢复计划2. 非计划内的故…

重写Sylar基于协程的服务器(0、搭建开发环境以及项目框架 || 下载编译简化版Sylar)

重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 重写Sylar基于协程的服务器系列&#xff1a; 重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 前言 sylar是…

计网Lesson11 - 虚拟机网络环境及socket概述

文章目录 虚拟机的简述socket概述 虚拟机的简述 放张图在这&#xff0c;根本没明白是啥对啥&#xff0c;以后学了Linux再来吧 &#x1f626; socket概述 s o c k e t socket socket 是一种用于应用层的用户态与应用层以下的内核态交互的工具&#xff0c;本意为“插座”。 也就是…

网络协议与攻击模拟_08DHCP协议

技术学习要了解某项技术能干什么&#xff1f;它的详细内容&#xff1f;发展走向&#xff1f; 一、DHCP协议 1、DHCP基本概念 dhcp动态主机配置协议&#xff0c;广泛应用于局域网内部 主要是为客户机提供TCP/IP 参数&#xff08;IP地址、子网掩码、网关、DNS等&#xff09;…