Java框架设计方案之ddd中领域事件发布的时机

发布时间 2024-01-08 14:21:03作者: wdss

领域事件作为ddd的核心组件之一,在ddd框架中处于一个重要地位,也是开发中非常常用的功能之一。

在基于Spring boot的框架开发中,Spring自带的事件可以很好地作为领域事件的基础(功能齐全且自带事务支持)。但开发框架过程中,遇到了一个设计难点。即:领域事件的发布时机。

 

1、抛开编程语言,从自然的角度出发,领域事件应该在触发该事件的业务发生时发布。例如:

UserAggregateRoot userAggr = createUser();

eventPublisher.publishEvent(new UserRegisteredEvent(userAggr));

但在实际项目中,上述方式显然有很大问题,因为此时业务发生在聚合根中,而该聚合根还未保存到数据库。而监听事件方会触发数据库更新,这将导致数据出现严重错误。

 

2、那么把领域事件的发布放到保存之后是否可行?例如:

User user = userRepository.save(userAggr);

eventPublisher.publishEvent(new UserRegisteredEvent(user));

在功能上,该方式可行。但是,它不符合ddd分层设计。

ddd分层设计思路是,领域层处理主要业务逻辑,服务层处理业务编排,仓储层处理数据持久化。领域事件的发布显然属于业务编排范围,但在这个方案中,事件发布代码放在了仓储层,这显然会给开发带来很大的混乱,所以这个方式也不好。

 

3、把领域事件暂时存储到公共变量中,等触发了持久化动作后,统一发布事件。例如:

UserAggregateRoot userAggr = createUser();

threadLocalEvents.add(new UserRegisteredEvent(userAggr));

...

userRepository.save(userAggr);

eventPublisher.publishEvent(threadLocalEvents);

该方式可行,但感觉仍不是最好的方案。

 

4、在参考了很多资料后,最终决定把领域事件存储到聚合根中,等触发了持久化动作后,统一发布事件。例如:

UserAggregateRoot userAggr = createUser();

public void createUser(){

  this.addEvent(new UserRegisteredEvent(this));

}

...

userRepository.save(userAggr);

最后由框架来实现eventPublisher.publishEvent(userAggr.getEvents());

该方案的优点是:没有违反ddd分层设计,且代码结构清晰。