领域驱动设计:领域事件

news2024/11/24 15:25:41

文章目录

    • 领域事件
    • 识别领域事件
    • 领域事件相关案例
    • 领域事件总体架构

领域事件

  • 领域事件是领域模型中非常重要的一部分,用来表示领域中发生的事件。一个领域事件将导致进一步的业务操作,在实现业务解耦的同时,还有助于形成完整的业务闭环。
    • 举例来说的话,领域事件可以是业务流程的一个步骤,比如投保业务缴费完成后,触发投保单转保单的动作;也可能是定时批处理过程中发生的事件,比如批处理生成季缴保费通知单,触发发送缴费邮件通知操作;或者一个事件发生后触发的后续动作,比如密码连续输错
      三次,触发锁定账户的动作。

识别领域事件

  • 在做用户旅程或者场景分析时,我们要捕捉业务、需求人员或领域专家口中的关键词:“如果发生……,则……”“当做完……的时候,请通知……”“发生……时,则……”等。在这些场景中,如果发生某种事件后,会触发进一步的操作,那么这个事件很可能就是领域事件。
  • 聚合的一个设计原则:在边界之外使用最终一致性。一次事务最多只能更改一个聚合的状态。如果一次业务操作涉及多个聚合状态的更改,应采用领域事件的最终一致性。
  • 领域事件驱动设计可以切断领域模型之间的强依赖关系,事件发布完成后,发布方不必关心后续订阅方事件处理是否成功,这样可以实现领域模型的解耦,维护领域模型的独立性和数据的一致性。在领域模型映射到微服务系统架构时,领域事件可以解耦微服务,微服务之间的数据不必要求强一致性,而是基于事件的最终一致性。

在具体的业务场景,有的领域事件发生在微服务内的聚合之间,有的则发生在微服务之间,还有两者皆有的场景,一般来说跨微服务的领域事件处理居多。在微服务设计时不同领域事件的处理方式会不一样。

微服务内的领域事件

  • 当领域事件发生在微服务内的聚合之间,领域事件发生后完成事件实体构建和事件数据持久化,发布方聚合将事件发布到事件总线,订阅方接收事件数据完成后续业务操作。
  • 微服务内大部分事件的集成,都发生在同一个进程内,进程自身可以很好地控制事务,因此不一定需要引入消息中间件。但一个事件如果同时更新多个聚合,按照 DDD“一次事务只更新一个聚合”的原则,你就要考虑是否引入事件总线。但微服务内的事件总线,可能会增加开发的复杂度,因此你需要结合应用复杂度和收益进行综合考虑。
  • 微服务内应用服务,可以通过跨聚合的服务编排和组合,以服务调用的方式完成跨聚合的访问,这种方式通常应用于实时性和数据一致性要求高的场景。这个过程会用到分布式事务,以保证发布方和订阅方的数据同时更新成功。

微服务之间的领域事件

  • 跨微服务的领域事件会在不同的限界上下文或领域模型之间实现业务协作,其主要目的是实现微服务解耦,减轻微服务之间实时服务访问的压力。
  • 领域事件发生在微服务之间的场景比较多,事件处理的机制也更加复杂。跨微服务的事件可以推动业务流程或者数据在不同的子域或微服务间直接流转。
  • 跨微服务的事件机制要总体考虑事件构建、发布和订阅、事件数据持久化、消息中间件,甚至事件数据持久化时还可能需要考虑引入分布式事务机制等。
  • 微服务之间的访问也可以采用应用服务直接调用的方式,实现数据和服务的实时访问,弊端就是跨微服务的数据同时变更需要引入分布式事务,以确保数据的一致性。分布式事务机制会影响系统性能,增加微服务之间的耦合,所以我们还是要尽量避免使用分布式事务。

领域事件相关案例

  • 介绍一个保险承保业务过程中有关领域事件的案例。
    • 一个保单的生成,经历了很多子域、业务状态变更和跨微服务业务数据的传递。这个过程会产生很多的领域事件,这些领域事件促成了保险业务数据、对象在不同的微服务和子域之间的流转和角色转换。在下面这张图中,列出了几个关键流程,用来说明如何用领域事件驱动设计来驱动承保业务流程。

在这里插入图片描述

  • 事件起点:客户购买保险 - 业务人员完成保单录入 - 生成投保单 - 启动缴费动作。
    1. 投保微服务生成缴费通知单,发布第一个事件:缴费通知单已生成,将缴费通知单数据发布到消息中间件。收款微服务订阅缴费通知单事件,完成缴费操作。缴费通知单已生成,领域事件结束。
    2. 收款微服务缴费完成后,发布第二个领域事件:缴费已完成,将缴费数据发布到消息中间件。原来的订阅方收款微服务这时则变成了发布方。原来的事件发布方投保微服务转换为订阅方。投保微服务在收到缴费信息并确认缴费完成后,完成投保单转成保单的操作。缴费已完成,领域事件结束。
    3. 投保微服务在投保单转保单完成后,发布第三个领域事件:保单已生成,将保单数据发布到消息中间件。保单微服务接收到保单数据后,完成保单数据保存操作。保单已生成,领域事件结束。
    4. 保单微服务完成保单数据保存后,后面还会发生一系列的领域事件,以并发的方式将保单数据通过消息中间件发送到佣金、收付费和再保等微服务,一直到财务,完后保单后续所有业务流程。
  • 总之,通过领域事件驱动的异步化机制,可以推动业务流程和数据在各个不同微服务之间的流转,实现微服务的解耦,减轻微服务之间服务调用的压力,提升用户体验。

领域事件总体架构

  • 领域事件的执行需要一系列的组件和技术来支撑。领域事件处理包括:事件构建和发布、事件数据持久化、事件总线、消息中间件、事件接收和处理等。 领域事件总体技术架构图:

在这里插入图片描述
事件构建和发布

  • 事件基本属性至少包括:事件唯一标识、发生时间、事件类型和事件源,其中事件唯一标识应该是全局唯一的,以便事件能够无歧义地在多个限界上下文中传递。事件基本属性主要记录事件自身以及事件发生背景的数据。
  • 另外事件中还有一项业务属性,用于记录事件发生那一刻的业务数据,这些数据会随事件传输到订阅方,以开展下一步的业务操作。
  • 事件基本属性和业务属性一起构成事件实体,事件实体依赖聚合根。领域事件发生后,事件中的业务数据不再修改,因此业务数据可以以序列化值对象的形式保存,这种存储格式在消息中间件中也比较容易解析和获取。
  • 事件发布之前需要先构建事件实体并持久化。事件发布的方式有很多种,你可以通过应用服务或者领域服务发布到事件总线或者消息中间件,也可以从事件表中利用定时程序或数据库日志捕获技术获取增量事件数据,发布到消息中间件。

事件数据持久化

  • 事件数据持久化可用于系统之间的数据对账,或者实现发布方和订阅方事件数据的审计。当遇到消息中间件、订阅方系统宕机或者网络中断,在问题解决后仍可继续后续业务流转,保证数据的一致性。
  • 事件数据持久化有两种方案:
    • 持久化到本地业务数据库的事件表中,利用本地事务保证业务和事件数据的一致性。
    • 持久化到共享的事件数据库中。这里需要注意的是:业务数据库和事件数据库不在一个数据库中,它们的数据持久化操作会跨数据库,因此需要分布式事务机制来保证业务和事件数据的强一致性,结果就是会对系统性能造成一定的影响。

事件总线

  • 事件总线是实现微服务内聚合之间领域事件的重要组件,它提供事件分发和接收等服务。事件总线是进程内模型,它会在微服务内聚合之间遍历订阅者列表,采取同步或异步的模式传递数据。事件分发流程大致如下:
    • 如果是微服务内的订阅者(其它聚合),则直接分发到指定订阅者;
    • 如果是微服务外的订阅者,将事件数据保存到事件库(表)并异步发送到消息中间件;
    • 如果同时存在微服务内和外订阅者,则先分发到内部订阅者,将事件消息保存到事件库(表),再异步发送到消息中间件。

消息中间件

  • 跨微服务的领域事件大多会用到消息中间件,实现跨微服务的事件发布和订阅。消息中间件的产品非常成熟,市场上可选的技术也非常多,比如 Kafka,RabbitMQ 等。

事件接收和处理

  • 微服务订阅方在应用层采用监听机制,接收消息队列中的事件数据,完成事件数据的持久化后,就可以开始进一步的业务处理。领域事件处理可在领域服务中实现。

领域事件驱动是很成熟的技术,在很多分布式架构中得到了大量的使用。领域事件是 DDD 的一个重要概念,在设计时我们要重点关注领域事件,用领域事件来驱动业务的流转,尽量采用基于事件的最终一致,降低微服务之间直接访问的压力,实现微服务之间的解耦,维护领域模型的独立性和数据一致性。
除此之外,领域事件驱动机制可以实现一个发布方 N 个订阅方的模式,这在传统的直接服务调用设计中基本是不可能做到的。

你知道的越多,你不知道的越多。

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

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

相关文章

脚本:python实现动态爱心

文章目录 效果代码Reference python实现dynamic heart 效果 代码 import turtle as tu import random as ratu.setup(0.5, 0.5) # 设置画板大小(小数表示比例,整数表示大小) tu.screensize(1.0, 1.0) # 设置屏幕大小 tu.bgcolor(black) #…

Linux安装logstash

相关链接 项⽬主⻚: https://www.elastic.co/cn/downloads/logstash 下载地址: wget https://artifacts.elastic.co/downloads/logstash/logstash-7.5.1.tar.gz 官网下载可能比较慢,下面提供下载地址 百度云链接:https://pan.…

C# wpf 实现桌面放大镜

文章目录 前言一、如何实现?1、制作无边框窗口2、Viewbox放大3、截屏显示(1)、截屏(2)、转BitmapSource(3)、显示 4、定时截屏 二、完整代码三、效果预览总结 前言 做桌面截屏功能时需要放大镜…

【关于Java:认识异常】

文章目录 一、1. 异常概念与体系结构1.1 异常的概念1.2 常见的异常1.算数异常2.数组越界异常3.空指针异常 1.3 异常的体系结构1.4 异常的分类1. 编译时异常2. 运行时异常(RuntimeException) 二、 异常的处理方式2.1 防御式编程2.2 EAFP:(异常…

API 架构学习

MQTT架构 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在…

uni-app运行到微信开发者工具-没有打印的情况

前言 到我们进场使用微信开发者工具时,就会发现它经常会有bug,特别是在软件更新,组件库更新之后 最近在更新微信开发者工具之后发现所有打印都不显示了,虽然是小问题-但对于强迫症很烦 以为是代码配置问题-结果是更新之后打印开…

代码随想录算法训练营第五十八天 | 739. 每日温度,496.下一个更大元素 I

代码随想录算法训练营第五十八天 | 739. 每日温度,496.下一个更大元素 I 739. 每日温度496.下一个更大元素 I 739. 每日温度 题目链接 视频讲解 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answe…

MySQL基础与库的基本操作

目录 1 MySQL基础一种存储解决方案SQL分类查看MySQL存储引擎 2 MySQL 库的操作数据库基本增删认识系统编码校验规则对数据库的影响数据库的查看与删除修改数据库数据库的备份与恢复查看连接情况 1 MySQL基础 一种存储解决方案 mysql本质是一种网络服务 mysql – 数据库服务的…

华为Mate60Pro携麒麟芯片回归,下半年国内手机市场出货量有望提升

本文由群狼调研**(长沙产品价格监测)**出品,欢迎转载,请注明出处。8月29日中午,华为以“Mate 60 Pro先锋计划”方式让Mate 60 Pro悄然开卖,线上线下用户纷纷前往抢购,据购买到新机的网友网络测速…

2672. 有相同颜色的相邻元素数目;1947. 最大兼容性评分和;958. 二叉树的完全性检验

2672. 有相同颜色的相邻元素数目 核心思想:枚举。每次操作只会影响index左右两边的数,所以我们只需要判断操作前index左右是否存在相同的数,然后减少一;然后将颜色修改,然后判断修改后index左右相邻的数是否是相同的&…

极致精细的jmeter+ant+jenkins 搭建接口自动化测试

一、jmeter 相信大家对jmeter并不陌生哈,如果没有安装和配置环境的小伙伴,可以直接找到我哈,我发给你。 二、ant 安装ant 第一步:下载ant http://ant.apache.org/ 第二步:配置ant window中设置ant环境变量&…

【C语言】每日一题(杨氏矩阵查找数)

目录 杨氏矩阵介绍:方法:思路:代码实现: 杨氏矩阵介绍: 既然在杨氏矩阵中查找数,那什么是杨氏矩阵呢? 矩阵的每行从左到右是递增的,矩阵从上到下是递增的。 例如: 方法…

四)Stable Diffussion使用教程:图生图

这一篇来说说图生图。 除了文生图之外,SD常用的还有图生图模式。 图生图,顾名思义就是使用一张图去让AI生成自己喜欢的另一张图。 有时候我们有一张喜欢的图,但是希望换一种颜色方案,这时就可以通过图生图的方式去实现了&#…

Python Asyncio 调用 CPU 多核工作

前言 Python 的 Asyncio 适合异步处理 IO 密集型的事务, 比如 HTTP 请求, 磁盘读写, 数据库操作等场景. 如果使用传统的顺序执行代码, 需要等待每次 IO 事务进行完成后才可以继续后面的代码. 通过在定义函数时添加修饰词 async 可以将其设置为异步函数, 后续配合 Asyncio 可以…

【MySQL】MySQL对于千万级的数据库或者大表怎么处理?

大致的思路 第一优化你的sql和索引; 第二加缓存,memcached,redis; 第三以上都做了后,还是慢,就做主从复制或主主复制,读写分离,可以在应用层做,效率高,也可以用三方工…

前端面试题JS篇(3)

["1", "2", "3"].map(parseInt) 答案是多少? 答案:[1, NaN, NaN] 原因:[1, NaN, NaN]因为 parseInt 需要两个参数(val, radix),其中 radix 表示解时用的基数。 map 传了 3 个(element, index, array) &…

【好书推荐】《速学Linux:系统应用从入门到精通》

目录 前言一、为什么学习Linux系统二、Linux系统的应用领域1.Linux在服务器的应用2.嵌入式Linux的应用3.桌面Linux的应用 三、Linux的版本选择1、经验人士使用的Debian2、以桌面应用为主的Ubuntu3、以经典桌面配置为主的Mint4、社区企业操作系…

CTFhub_SSRF靶场教程

CTFhub SSRF 题目 1. Bypass 1.1 URL Bypass 请求的URL中必须包含http://notfound.ctfhub.com,来尝试利用URL的一些特殊地方绕过这个限制吧 1.利用?绕过限制urlhttps://www.baidu.com?www.xxxx.me 2.利用绕过限制urlhttps://www.baidu.comwww.xxxx.me 3.利用斜…

ctfshow-web-【nl】难了

0x00 前言 CTF 加解密合集CTF Web合集网络安全知识库 文中工具皆可关注 皓月当空w 公众号 发送关键字 工具 获取 0x01 题目 0x02 Write Up 这里首先看到只要当1小于4的时候才可以执行命令。这里看到题目【nl】,先查一下nl的含义,这个命令是将文件里的…

Eureka 笔记

服务端&#xff1a; 创建springBoot 项目 1.步骤 导入在pom.xml中 导入 eureka-server的 jar包 2.步骤 在主方法加注解 EnableEurekaServer 3. 步骤 在配置config 1.步骤pox.xml: <?xml version"1.0" encoding"UTF-8"?&g…