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

时间:2015-10-14 06:14:52

标签: java spring transactions event-handling domain-driven-design

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

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

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

现在我的问题是:

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

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

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

1 个答案:

答案 0 :(得分:9)

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

  • P1:已发布的域事件必须描述真正发生的更改(即确保没有鬼事件开始飞来飞去)。
  • P2:对触发域事件必须的数据库所做的更改会导致事件被发布(即不会丢失事件)。

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

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

  2. 使用2阶段提交。我不会觉得这种情况不再使用了,但也许它不那么受欢迎了,因为它并不是一种奇特的技术......

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

    • 始终先同步发布事件,然后保留到数据库。这确保P2成立。
    • 然后使用事件处理器检查事件流并检查是否可以在数据库中找到事件。如果没有,请从流中删除该事件。这确保P1成立。
  4. 解决方案3需要仔细设计并检查系统的每个部分在故障行为方面所做的保证,因此可能是最难实现的。但是一旦它起作用,它也是一个非常优雅的解决方案。

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