DDD-领域事件

领域事件

领域事件是领域模型中非常重要的一部分,用来表示领域中发生的事件。一个领域事件将导致进一步的业务操作,在实现业务解耦的同时,还有助于形成完整的业务闭环。

领域事件驱动设计可以切断领域模型之间的强依赖关系,事件发布完成后,发布方不必关心后续订阅方事件处理是否成功,这样可以实现领域模型的解耦,维护领域模型的独立性和数据的一致性。在领域模型映射到微服务系统架构时,领域事件可以解耦微服务,微服务之间的数据不必要求强一致性,而是基于事件的最终一致性。

一次事务最多只能更改一个聚合的状态。如果一次业务操作涉及多个聚合状态的更改,应采用领域事件的最终一致性。

微服务内领域事件

当领域事件发生在微服务内的聚合之间,领域事件发生后完成事件实体构建和事件数据持久化,发布方聚合将事件发布到事件总线,订阅方接收事件数据完成后续业务操作。

在一个进程中的领域事件一般不需要使用消息中间件,可以使用类似Guava EvenBus的第三方事件总线。

微服务间领域事件

跨微服务的领域事件会在不同的限界上下文或领域模型之间实现业务协作,其主要目的是实现微服务解耦,减轻微服务之间实时服务访问的压力。

跨微服务的事件机制要总体考虑事件构建、发布和订阅、事件数据持久化、消息中间件,甚至事件数据持久化时还可能需要考虑引入分布式事务机制等。

领域事件架构

1. 事件构建和发布

领域事件包括基本属性和业务属性,事件实体依赖聚合根。领域事件发生后,事件中的业务数据不再修改。

基本属性:至少包括事件唯一标识、发生时间、事件类型和事件源,其中事件唯一标识应该是全局唯一的,以便事件能够无歧义地在多个限界上下文中传递。事件基本属性主要记录事件自身以及事件发生背景的数据。

业务属性:用于记录事件发生那一刻的业务数据,这些数据会随事件传输到订阅方,以开展下一步的业务操作。

可以创建基类来保证事件结构的统一,如下:

1
2
3
4
5
6
7
public class DomainEvent<T> {
private String eventId;
private long timestamp;
private String eventType;
private String source;
private T data;
}

2. 事件数据持久化

事件数据持久化可用于系统之间的数据对账,或者实现发布方和订阅方事件数据的审计。当遇到消息中间件、订阅方系统宕机或者网络中断,在问题解决后仍可继续后续业务流转,保证数据的一致性。

对于领域事件敏感的业务,可以将事件持久化到数据库:

  • 持久化到本业务数据库中,利用本地事务保证业务和事件数据的一致性
  • 持久化到共享的事件数据库中,业务数据库和事件数据库不在一个数据库中,它们的数据持久化操作会跨数据库,可能需要分布式事务保证事件和业务的一致性

对于领域事件不太敏感的业务,可以将事件异步的同步到日志中心或离线数仓中

3. 事件总线

事件总线是进程内模型,是实现服务内聚合之间领域事件的重要组件,它提供事件分发和接收等服务。流程大致如下:

  • 如果是微服务内的订阅者(其它聚合),则直接分发到指定订阅者
  • 如果是微服务外的订阅者,将事件数据保存到事件库(表)并异步发送到消息中间件
  • 如果同时存在微服务内和外订阅者,则先分发到内部订阅者,将事件消息保存到事件库(表),再异步发送到消息中间件

4. 消息中间件

跨微服务的领域事件大多会用到消息中间件,实现跨微服务的事件发布和订阅。

5. 事件接收和处理

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

分享到: