如何在DDD中管理域逻辑和事件之间的事务?

我正在研究DDD和事件源中的编程。

我看到一个例子,当一个域逻辑被调用时(例如Order.placeOrder() ),它会发布一个事件(例如OrderPlaced )。 事件将作为事件存储发送到MQ。

域逻辑( Order.placeOrder() )应该是一个primefacesAPI,如果使用Spring作为事务管理器,它应该有@Transactional注释。

现在我的问题是:

  1. 如何确保数据库更改和事件发送在同一个事务中? 即如果在将数据提交到DB时出现任何错误,则该事件永远不应发送给MQ。

    我知道有像XA或2阶段提交这样的解决方案来强制数据库更新并在同一事务中发送MQ消息。 但似乎现在还没有广泛使用。

  2. 如果仍然使用Spring @Transactional注释而没有XA,那么在事务成功提交后我们可能会做一些逻辑吗? 这样做的最佳做法是什么?

必须具备以下两个属性才能拥有可靠的系统:

  • P1:发布的域事件必须描述真正发生的变化(即确保没有鬼事件开始飞来飞去)。
  • P2:对触发域事件的DB的更改必须导致发布事件(即不丢失事件)。

实现这一目标有以下几种可能性,所有这些都是我自己使用过或看到过在项目中使用的:

  1. 使用与应用程序使用相同数据库的消息传递基础结构,以便可以使用单个事务。 当一个非常简单的消息传递基础设施足够时,该解决方案是可行的,并且团队决定自己构建它。

  2. 使用2阶段提交。 我不会觉得这个不再使用了,但也许它不那么受欢迎了,因为它不是花哨的技术……

  3. 使用一些聪明的技巧来确保两种条件都成立。 例如我称之为鸡肉和鸡蛋的解决方案:

    • 始终首先同步发布事件,然后持久保存到数据库。 这确保P2成立。
    • 然后使用事件处理器检查事件流并检查是否可以在DB中找到事件。 如果没有,请从流中删除该事件。 这确保P1成立。

解决方案3需要仔细设计并检查系统的每个部分在故障行为方面所做的保证,因此可能是最难实现的。 但是一旦它起作用,它也是一个非常优雅的解决方案。

顺便说一句,我不同意将Spring注释添加到域对象,而是添加到相应的应用服务。 这仅作为旁注。