DDD—领域事件,Guava EventBus
一、领域事件
领域内的业务行为发生后通常会触发进一步的业务操作,DDD中这类事件被称为领域事件。比如电商领域,订单下单后触发支付动作,支付动作完成后触发仓库出货动作,出货完成触发快递运输动作,这些都是业务流程中的步骤,以领域事件表示。
在和业务专家沟通的过程中,诸如一些他们聊到的关键词“如果这样,就那样”,“这个完了就进行那个”,“发生xx得通知xxx”,类似这种关键词时,则需要捕捉到这部分就是业务流程中的领域事件
二、DDD为什么需要领域事件驱动
领域事件发生后,以Spring似的的强依赖方式,引用下游事件进行处理,这样会造成领域之间的强依赖。
领域事件驱动,可以切断领域模型之间的强依赖关系,维护领域模型的独立,实现领域模型的解耦,当领域模型映射到微服务实现时,则实现了微服务之间的解耦。这样微服务之间不再需要分布式的强一致性事务,而是可以实现基于消息的数据最终一致性。
三、领域事件解耦微服务
领域事件解耦的关键点在于,事件发布方完成自己的业务逻辑后,发送通知下一个领域模型执行操作,这中间过程发布方可以不关心订阅方的事件处理是否成功,实现领域间的解耦,这个领域可以是服务内聚合与聚合之间,也可以是多个微服务之间,数据的一致性也可以基于消息的最终一致性方案完成。
1、服务内实现事件驱动
(1)事件通知——观察者模式
参考博客内对观察者模式的讲解:
https://www.cnblogs.com/jiyukai/p/14786534.html
(2)事件通知——Guava EventBus
Guava EventBus允许在组件之间进行发布-订阅式的通信,参考博客内对Guava EventBus的讲解:
https://www.cnblogs.com/jiyukai/p/14828564.html
(3)数据一致性——服务内强一致性
在领域模型内,如果需要跨多个聚合实现业务逻辑时,要么通过事件总线实现微服务内的多聚合数据的最终一致性,要么通过事务机制保证数据的强一致性。
领域逻辑不涉及到事务回滚的:事件总线解决方案:
在采用事件总线进行领域事件处理时,可以根据需要完成领域事件实体的构建,然后发布方聚合会将领域事件发布到事件总线,订阅方聚合接收领域事件后完成后续业务处理。事件总线的设计方式存在开发复杂度,需要结合实际情况具体分析
领域逻辑涉及事务回滚的:应用层采用事务机制保证数据的强一致性:
在应用层增加事务控制,在对多个聚合的领域服务进行组合和编排时,通过事务机制来确保多个聚合在提交数据时实现数据强一致性。这种方式应用于实时性和数据一致性要求高的场景。
2、服务间实现事件驱动
(1)事件通知——MQ
服务间涉及事务的事件驱动,可以采用分布式事务技术来保证服务间的数据强一致性,但分布式事务基于二阶段提交的过程会严重影响系统性能,同时增加微服务之间的耦合。
微服务之间的领域事件可以采用异步化的最终一致性设计,基于消息中间件的事件驱动,实现微服务的解耦,推动业务流程在不同的子域或微服务之间进行流转,减轻微服务同步访问压力的同时,也避免了很多情况下某个微服务宕机导致的雪崩效应。
(2)数据一致性——服务间基于消息的最终一致性
参考博客内对最终一致性的讲解:
https://www.cnblogs.com/jiyukai/p/9463626.html
3、领域事件驱动实现细节
- 事件实体的基本属性应该包括事件唯一标识,发生时间,事件类型和事件源,记录事件自身属性和发生背景相关的数据。
- 事件实体还记录了事件发生时相关的业务数据,业务数据随着事件发布传输到订阅方,由订阅方开展下一步业务操作
- 事件发布前,要把事件实体持久化到数据库事件表中(方便数据对账,问题溯源,防止服务宕机导致事件丢失),事件的发布可以通过事件总线,消息中间件直接投递或者采用定时轮询数据库日志捕获技术(CDC),获取增量事件数据,进行推送
- 事件发布在应用层实现,事件触发的业务逻辑实现在领域层实现。
参考书籍 ——《基于DDD和微服务的中台架构与实现》欧创新、邓頔
参考书籍 ——《领域驱动设计》Eric Evans
参考书籍 ——《架构真经》Martin L. Abbott