详解领域驱动设计之事件驱动与CQRS

编程之家收集整理的这篇文章主要介绍了详解领域驱动设计之事件驱动与CQRS编程之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

这篇文章分析了如何应用事件来分离软件核心复杂度。探究CQRS为什么广泛应用于DDD项目中,以及如何落地实现CQRS框架。当然我们也要警惕一些失败的教训,利弊分析以后再去抉择正确的应对之道

目录

一、前言:从物流详情开始

二、领域事件

2.1、建模领域事件

2.2、领域事件代码解读

2.3、领域事件的存储

2.3.1、单独的EventStore

2.3.2、与业务数据一起存储

2.4、领域事件如何发布

2.4.1、由领域聚合发送领域事件

2.4.2、事件总线VS消息中间件

三、Saga分布式事务

3.1、Saga概要

3.2、Saga实现

3.2.1、协同式(choreography)

3.2.2、编排式(orchestration)

3.2.3、补偿策略

四、CQRS

五、自治服务和系统

六、结语

一、前言:从物流详情开始

大家对物流跟踪都不陌生,它详细记录了在什么时间发生了什么,并且数据作为重要凭证是不可变的。我理解其背后的价值有这么几个方面:业务方可以管控每个子过程、知道目前所处的环节;另一方面,当需要追溯时候仅仅通过每一步的记录就可以回放整个历史过程。

我在之前的文章中提出过“软件项目也是人类社会生产关系的范畴,只不过我们所创造的劳动成果看不见摸不着而已”。所以我们可以借鉴物流跟踪的思路来开发软件项目,把复杂过程拆解为一个个步骤、子过程、状态,这和我们事件划分是一致的,这就是事件驱动的典型案例。

二、领域事件

领域事件(Domain Events)是领域驱动设计(Domain Driven Design,DDD)中的一个概念,用于捕获我们所建模的领域中所发生过的事情。

领域事件本身也作为通用语言(Ubiquitous Language)的一部分成为包括领域专家在内的所有项目成员的交流用语。

比如在前述的跨境物流例子中,货品达到保税仓以后需要分派工作人员进行分拣分包,那么“货品已到达保税仓”便是一个领域事件。

首先,从业务逻辑来说该事件关系到整个流程的成功或者失败;同时又将触发后续子流程;而对于业务方来说,该事件也是一个标志性的里程碑,代表自己的货品就快配送到自己手中。

所以通常来说,一个领域事件具有以下几个特征:较高的业务价值,有助于形成完整的业务闭环,将导致进一步的业务操作。这里还要强调一点,领域事件具有明确的边界。

比如:如果你建模的是餐厅的结账系统,那么此时的“客户已到达”便不是你关心的重点,因为你不可能在客户到达时就立即向对方要钱,而“客户已下单”才是对结账系统有用的事件。

2.1、建模领域事件

在建模领域事件时,我们应该根据限界上下文中的通用语言来命名事件及属性。如果事件由聚合上的命令操作产生,那么我们通常根据该操作方法的名字来命名领域事件。

对于上面的例子“货品已到达保税仓”,我们将发布与之对应的领域事件

GoodsArrivedBondedWarehouseEvent(当然在明确的界限上下文中也可以去掉聚合的名字,直接建模为ArrivedBondedWarehouseEvent,这都是命名方面的习惯)。

事件的名字表明了聚合上的命令方法在执行成功之后所发生的事情,换句话说待定项以及不确定的状态是不能作为领域事件的。

一个行之有效的方法是画出当前业务的状态流转图,包含前置操作以及引起的状态变更,这里表达的是已经变更完成的状态所以我们不用过去时态表示,比如删除或者取消,即代表已经删除或者已经取消。

然后对于其中的节点进行事件建模。如下图是文件云端存储的业务,我们分别对预上传上传完成确认、删除等环节建模“过去时”事件,PreUploadedEvent、ConfirmUploadedEvent、RemovedEvent。

2.2、领域事件代码解读

package domain.event; import java.util.Date; import java.util.UUID; public class DomainEvent { /** * 领域事件还包含了唯一ID, * 但是该ID并不是实体(Entity)层面的ID概念, * 而是主要用于事件追溯和日志。 * 如果是数据库存储,该字段通常为唯一索引。 */ private final String id; /** * 创建时间用于追溯,另一方面不管使用了 * 哪种事件存储都有可能遇到事件延迟, * 我们通过创建时间能够确保其发生顺序。 */ private final Date occurredOn; public DomainEvent() { this.id = String.valueOf(UUID.randomUUID()); this.occurredOn = new Date(); } }

在创建领域事件时,需要注意2点:

领域事件本身应该是不变的(Immutable);

领域事件应该携带与事件发生时相关的上下文数据信息,但是并不是整个聚合根的状态数据。例如,在创建订单时可以携带订单的基本信息,而对于用户更新订单收货地址事件AddressUpdatedEvent事件,只需要包含订单、用户以及新的地址等信息即可。

public class AddressUpdatedEvent extends DomainEvent { //通过userId+orderId来校验订单的合法性; private String userId; private String orderId; //新的地址 private Address address; //略去具体业务逻辑 }

2.3、领域事件的存储

事件的不可变性与可追溯性都决定了其必须要持久化的原则,我们来看看常见的几种方案。

2.3.1、单独的EventStore

有的业务场景中会创建一个单独的事件存储中心,可能是MysqL、Redis、Mongo、甚至文件存储等。这里以MysqL举例,business_code、event_code用来区分不同业务的不同事件,具体的命名规则可以根据实际需要。

这里需要注意该数据源与业务数据源不一致的场景,我们要确保当业务数据更新以后事件能够准确无误的记录下来,实践中尽量避免使用分布式事务,或者尽量避免其跨库的场景,否则你就得想想如何补偿了。千万要避免,用户更新了收货地址,但是AddressUpdatedEvent事件保存失败。

总的原则就是对分布式事务Say No,无论如何,我相信方法总比问题多,在实践中我们总可以想到解决方案,区别在于该方案是否简洁、是否做到了解耦。

# 考虑是否需要分表,事件存储建议逻辑简单 CREATE TABLE `event_store` ( `event_id` int(11) NOT NULL auto increment, `event_code`上一篇:编程语言榜单Java与Python并列第二!Julia下滑下一篇:Java动态代理四种实现方式详解 热门搜索

领域驱动设计 

领域驱动 

事件驱动 

事件驱动型 

事件驱动引擎 

相关文章

详解领域驱动设计之事件驱动与CQRS

2021-09-10阅读(5571)评论(0)推荐()

这篇文章分析了如何应用事件来分离软件核心复杂度。探究CQRS为什么广泛应用于DDD项目中,以及如何落地实现CQRS框架。当然我们也要警惕一些失败的教训,利弊分析...

浅谈Java开发架构之领域驱动设计DDD落地

2021-10-10阅读(9494)评论(0)推荐()

DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的...

PHP事件驱动化设计详解

2021-10-16阅读(9715)评论(0)推荐()

这篇文章主要介绍了PHP事件驱动化设计,结合实例形式较为详细的分析了PHP事件驱动化所涉及的信号量、共享内存与进程间通信相关概念与操作技巧,需要的朋友可以参考下

详解Javascript事件驱动编程

2021-10-05阅读(4146)评论(0)推荐()

这篇文章主要为大家详细介绍了Javascript事件驱动编程的相关资料,通过经典案例向大家介绍Javascript事件驱动编程,需要的朋友可以参考下

Node.js中的事件驱动编程详解

2021-10-05阅读(7783)评论(0)推荐()

这篇文章主要介绍了Node.js中的事件驱动编程详解,本文主要讲解理论性知识,如什么是事件驱动编程、什么是闭包、闭包如何帮助异步编程等知识,需要的朋友可以参考下

快速掌握Node.js事件驱动模型

2021-10-05阅读(8267)评论(0)推荐()

这篇文章主要为大家详细介绍了Node.js事件驱动模型,首先了解一下传统的线程网络模型,然后再学习了解Node.js事件驱动模型,感兴趣的小伙伴们可以参考一下

浅谈JS和Nodejs中的事件驱动

2021-09-16阅读(2745)评论(0)推荐()

这篇文章主要介绍了JS和Nodejs中的事件驱动,对事件驱动感兴趣的同学,可以参考下

取消

有人回复邮件通知

提交评论

© 2021 编程之家 

工信部备案号:琼ICP备2022000316号

总结

以上是编程之家为你收集整理的详解领域驱动设计之事件驱动与CQRS全部内容。

如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给好友。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

Java相关文章

Java是目前世界上最流行的计算机编程语言之一,也是很多人学IT时学习的主要语言,而且目前从PC端到移动平台都有着Java的身影,Java工程师的需求量依旧不低,在编程语言排行榜上依旧名列前茅。
为了提升处理效率,并发一直以来都是软件开发设计场景中无法绕过的话题。为了提升系统的整体并发吞吐量,程序员们可谓是煞费苦心。本文换个角度探讨下多线程并发相关的内容,全面了解下多线程并发世界的各种关联。
日期时间的处理,是软件开发中极其常见的场景,JAVA中与日期、时间相关的一些类与API方法也很多,这里结合平时的编码实践全面的整理了下,希望可以帮助大家厘清其中的门道,更加游刃有余的面对此方面的处理~
笔者结合在团队中多年的代码检视遇到的情况,结合平时项目编码实践经验,对Stream的核心要点与易混淆用法、典型使用场景等进行了详细的梳理总结,希望可以帮助大家对Stream有个更全面的认知,也可以更加
在分布式系统盛行的今天,缓存充当着扛压屏障的作用,一旦缓存出现问题,对系统影响也是致命的。本文我们一起聊聊如何安全且可靠的使用缓存,聊聊缓存击穿、缓存雪崩、缓存穿透以及数据一致性、热点数据淘汰机制等。
容器设计是项目编码中非常常见的一个场景,本文从项目中习以为常的一些场景作为切入点,聊一聊容器设计的一些思考、聊一聊元素遍历的思路、以及身为设计模式之一的迭代器模式。
在项目开发中,由于业务规划原因,经常会涉及到聚合信息处理类的场景,按照环节串行执行的时候往往最终响应耗时很长,JAVA对并行的处理场景支持已经很完善了,本文深度总结了应对策略,快来看看吧~
业界各大厂商或开源团队都会构建并提供一些缓存框架组件提供给开发者按需选择,这里就会涉及到一个标准规范的遵循问题,本文我们一起聊聊JCache API规范与SpringCache规范。