基于领域事件实现微服务解耦
领域事件是解耦微服务的关键。
什么是领域事件
除了命令和操作等业务行为,还有一种非常重要的事件,这种事件通常会导致进一步的业务操作,在DDD(Domain Driven Design,领域驱动设计)中,这种事件叫做 领域事件。
领域事件可以是业务流程中的一个步骤。比如,投保业务缴费之后,投保单转为保单。支付成功后,生产商品订单。这里的支付就是一个领域事件。或者是一个事件发生后触发进一步操作, 比如,密码连续输入错误3次,账户锁定。这里的密码输入就是一个领域事件。
如何识别领域事件
用户旅程或者场景分析时,捕捉业务/需求人员或者领域专家口中的关键词:
- 如果发生 。。。。,则。。。
- 完。。。。的时候,请通知。。。
- 发生。。。。时,则。。。
微服务之间的领域事件
跨微服务的领域事件会在不同界限上下文或领域模型直接实现业务协助,主要目的是实现微服务解耦。减轻微服务直接实现服务访问的压力。
举个例子:
当用户在购物车点击结算时,生成待付款订单,若支付成功,则更新订单状态为已支付,扣减库存,并推送捡货通知信息到捡货中心。
在你没有接触领域事件或EDA(事件驱动架构)之前,你会如何实现这个用例?肯定是简单直接的方法调用,在一个事务中分别去调用状态更新方法、扣减库存方法、发送捡货通知方法。
上面的实现有什么问题?
- 试想一下,若现在要求支付成功后,需要额外发送一条付款成功通知到微信公众号,我们怎么实现?想必我们需要额外定义发送微信通知的接口并封装参数,然后再添加对方法的调用。这种做法虽然可以解决需求的变更,但很显然不够灵活耦合性强,也违反了OCP。
- 将多个操作放在同一个事务中,使用事务一致性可以保证多个操作要么全部成功要么全部失败。在一个事务中处理多个操作,若其中一个操作失败,则全部失败。但是,这在业务上是不允许的。客户成功支付了,却发现订单依旧为待付款,这会导致纠纷的。
- 违反了聚合的一大原则:在一个事务中,只对一个聚合进行修改。在这个用例中,很明显我们在一个事务中对订单聚合和库存聚合进行了修改。
如果基于领域事件如何实现?
领域事件建模
- 事件源 entry
- 事件对象 domainMessage = 事件类型 eventTopic 事件源 entry
- 事件监听器 subscribe 处理事件
- 事件分发器 注册监听器 JvmEventConsumer
- 生产消息 实时消费,producer中直接consume 或者 MQ的形式,异步消费
领域事件 = 事件发布 事件存储 事件分发 事件处理。
为啥基于领域事件驱动的设计能够实现系统解耦?
关键是因为居于事件驱动架构 【Event-Driven Architecture(事件驱动架构))】
事件驱动架构有三个特性:
- 异步
- 实时
- 彻底解耦
EDA 架构的核心是基于消息的发布订阅模式,通过发布订阅,消息消费方对于消息发送方而言是完全透明的,发送方只是把消息正常发送到消息中间件,其他的不关心, 及时发送消息的时候,消息接收方不可用,消息生产者仍然可以发送消息,这样实现了系统间的彻底解耦,不存在系统间的依赖。
总结
领域事件是 DDD 的重要概念,设计时需要关注领域事件,用领域事件来驱动业务流转,尽量采用事件的最终一致性,降低微服务直接的耦合,实现微服务间的解耦,维护领域模型的独立性和数据一致性。
参考资料:
- https://www.cnblogs.com/davenkin/p/microservices-and-domain-events.html
- https://liqiang.io/post/ddd-practice-part-02-architecture-ac314d3d
- https://www.cnblogs.com/sheng-jie/p/7124727.html
- https://www.cnblogs.com/xishuai/p/iddd-cqrs-and-eda.html#xishuai_h2