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

news2024/11/18 1:38:17

目录

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.coloradmin.cn/o/1417914.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot内置工具类

Collections java.util包下的Collections类&#xff0c;该类主要用于操作集合或者返回集合 一、排序 List<Integer> list new ArrayList<>();list.add(2);list.add(1);list.add(3);Collections.sort(list);//升序System.out.println(list);Collections.reverse(…

vue实践:构建高效的电子签名功能

前言 在现代数字化时代&#xff0c;电子签名成为了一种方便、高效且安全的签署文件的方式。本文将介绍电子签名的原理和实现方法&#xff0c;帮助你快速掌握这一重要的工具。 电子签名是什么&#xff1f; 电子签名是一种数字化的签名方式&#xff0c;用于验证和确认电子文档、…

GitLab16.8配置webhooks、Jenkins2.4配置GitLab插件实现持续集成、配置宝塔面板实现持续部署(其三)

看本篇文章的前提是已经部署完GItlab和Jenkins服务器&#xff0c;已经可以手动构建成功&#xff0c;并且经过了很多次实践&#xff0c;对这两款软件基本熟悉。 建议大家按以下顺序看 前端自动化&#xff08;其一&#xff09;部署gitlab 前端自动化&#xff08;其二&#xff0…

第五篇:express路由路径方式(字符串,字符串模式,和正则)

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 引言&#xff1a; &#x1f4…

MYSQL基本查询(CURD:创建、读取、更新、删除)

文章目录 前言一、Create1.全列插入2.指定列插入3.插入否则更新4.替换 二、Retrieve1.SELECT列2.WHERE条件3.结果排序4.筛选分页结果 三、Update四、Delete1.删除数据2.截断表 五、插入查询结果六、聚合函数 前言 操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型…

用友U9 PatchFile.asmx 任意文件上传漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

CC攻击类型有几种?有什么有用的防御方法?

CC攻击&#xff08;Challenge Collapsar Attack&#xff09;是一种复杂的网络攻击&#xff0c;它利用了一种分布式拒绝服务攻击工具来发动攻击。这种攻击方式通常由黑客组织或恶意竞争者发起&#xff0c;旨在破坏目标网络或系统的正常运行。 CC攻击的核心在于使用大量的虚假请…

如何选择便捷安全的黄金交易平台?

黄金交易平台的介绍 黄金交易平台是一个提供方便、安全的方式进行黄金交易的网上平台。 投资者可以通过这些平台进行黄金的买卖&#xff0c;参与黄金市场的投资活动。 这些平台提供了一个简单易用的界面&#xff0c;让投资者可以方便地进行交易操作。 选择合适的黄金交易平台…

Kafka-服务端-GroupMetadataManager

GroupMetadataManager是GroupCoordinator中负责管理Consumer Group元数据以及其对应offset信息的组件。 GroupMetadataManager底层使用Offsets Topic,以消息的形式存储Consumer Group的GroupMetadata信息以及其消费的每个分区的offset,如图所示。 consumer_offsets的某Partiti…

LLM之Agent(九)| 通过API集成赋能Autogen Multi-Agent系统

随着大型语言模型的快速发展&#xff0c;构建基于LLM驱动的自治代理&#xff08;autonomous agents&#xff09;已经成为一个备受关注的话题。仅在过去一年中&#xff0c;就出现了许多基于这一理念的新技术和框架。 ​ 本文将探索微软开源的Agent框架&#xff1a;Autogen…

259:vue+openlayers: 显示海量多边形数据,10ms加载完成

第259个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中通过WebGLVectorLayerRenderer方式加载海量多边形数据。这里相当于将海量的数据放在同一个层的source中,然后通过webglTile的方式渲染出这一层。 本示例数据为5000个多边形,加载速度超级快。 直接…

D35XB80-ASEMI整流桥D35XB80参数、封装、尺寸

编辑&#xff1a;ll D35XB80-ASEMI整流桥D35XB80参数、封装、尺寸 型号&#xff1a;D35XB80 品牌&#xff1a;ASEMI 封装&#xff1a;GBJ-5 最大重复峰值反向电压&#xff1a;800V 最大正向平均整流电流(Vdss)&#xff1a;35A 功率(Pd)&#xff1a; 芯片个数&#xff1…

Web 开发 6:Redis 缓存(Flask项目使用Redis并同时部署到Docker详细流程 附项目源码)

大家好&#xff01;欢迎来到第六篇 Web 开发教程&#xff0c;今天我们将探讨一个非常重要的话题&#xff1a;Redis 缓存。作为一个互联网开发者&#xff0c;你一定知道在处理大量请求时&#xff0c;性能优化是至关重要的。而 Redis 缓存正是帮助我们提升系统性能的利器。Redis …

tcpdump 抓包无法落盘

文章目录 问题背景解决办法 问题背景 在嵌入式设备中(Linux系统)&#xff0c;为了分析两个网络节点的通讯问题&#xff0c;往往需要用到tcpdump&#xff0c;抓一个.pcap的包在PC端进行分析。博主在实际操作中发现&#xff0c;抓包无法实时落盘。 解决办法 # 下面的命令是写在…

MATLAB环境下一种贝叶斯稀疏盲反卷积算法

稀疏盲反卷积贝叶斯估计方法通常使用伯努利-高斯分布(BG)先验的稀疏序列建模&#xff0c;并利用马尔可夫链蒙特卡罗(MCMC)方法进行未知估计。然而&#xff0c;BG模型的离散性会有计算瓶颈。为了解决这个问题&#xff0c;提出一个替代方案&#xff0c;采用MCMC方法对稀疏序列进行…

Mac下查看、配置和使用环境变量

Mac下查看、配置和使用环境变量 一&#xff1a;Mac怎么查看环境变量命令 printenv一&#xff1a;这个命令会一次性列出所有环境变量的键值对&#xff0c;输出格式为&#xff1a; VAR1value1 VAR2value2 ...二&#xff1a; 也可以通过给这个命令加上环境变量名参数&#xff0…

Linux CPU 负载说明

一、背景 工作中我们经常遇到CPU 负载高&#xff0c;CPU负载高意味着什么&#xff1f; CPU的负载是怎么计算的&#xff1f; top指令中的各个指标代表什么含义&#xff1f; 二、CPU 负载计算方法 在系统出现负载问题&#xff0c;通常会使用uptime和top确认负载&#xff0c;这两…

云微呼云呼叫中心:颠覆传统客户服务模式的数字化创新

1. 引言 随着云计算技术的不断成熟和普及&#xff0c;云呼叫中心作为一种全新的客户服务解决方案&#xff0c;正在以其高效、灵活和智能的特点&#xff0c;逐渐颠覆传统的客户服务模式。本文将深入探讨云呼叫中心的定义、特点、优势以及对企业的重要意义。 2. 云呼叫中心的定…

龙行龘龘•前程朤朤 | 联诚发2023年度述职大会与年会圆满举行

1月27日上午&#xff0c;以“不忘初心・砥砺前行”为主题的2023年度述职大会在联诚发深圳总部隆重举行&#xff0c;本次大会总结了过去一年的成就与不足&#xff0c;展望了未来一年的方向和目标。联诚发创始人兼总裁龙平芳、董秘毛强军、各部门负责人以及深圳总部全体职工出席了…

JSP概述和基本使用

1&#xff0c;JSP 概述 JSP&#xff08;全称&#xff1a;Java Server Pages&#xff09;&#xff1a;Java 服务端页面。是一种动态的网页技术&#xff0c;其中既可以定义 HTML、JS、CSS等静态内容&#xff0c;还可以定义 Java代码的动态内容&#xff0c;也就是 JSP HTML Jav…