我正在学习DDD和事件源的编程。
我看到一个例子,当一个领域逻辑被调用时(例如Order.placeOrder()
),它将发布一个事件(例如OrderPlaced
)。事件将作为事件存储发送到MQ。
领域逻辑(Order.placeOrder()
)应该是一个原子API,如果使用Spring作为事务管理器,它应该有@Transactional
注释。
现在我的问题是:
-
如何确保DB更改和事件发送在同一事务内?例如,如果在向DB提交数据时出现任何错误,则事件永远不应发送到MQ。
我知道有像XA或2阶段提交这样的解决方案来强制DB更新并在同一事务中发送MQ消息。
-
如果仍然使用Spring
@Transactional
注释而没有XA,是否有可能在事务成功提交后我们做一些逻辑?最好的做法是什么?
一个可靠的系统必须具备以下两个属性:
- P1:已发布的域事件必须描述真正发生的变化(即确保没有幽灵事件开始四处飞行)。
- P2:触发域事件的DB更改必须导致事件被发布(即不要丢失事件)。
有以下几种可能性可以实现这一点,所有这些都是我自己使用或在项目中使用的:
-
使用与应用程序使用相同数据库的消息传递基础设施,以便可以使用单个事务。当一个非常简单的消息传递基础设施就足够了,并且团队决定自己构建它时,这个解决方案是可行的。
-
使用2阶段提交。我不觉得它已经不再被使用了,但也许它很少被谈论,因为它不是一项花哨的技术…
-
使用一些巧妙的技巧来确保两个条件都成立。例如,我称之为鸡和蛋的解决方案:
- 总是先同步发布事件,然后持久化到DB。这确保了P2保持不变。
- 然后使用事件处理器来检查事件流并检查是否可以在DB中找到事件。如果没有,则从流中删除该事件。这确保P1保持。
解决方案3需要仔细设计和审查系统的每个部分在故障行为方面所做的保证,因此它可能是最难正确处理的。但这也是一个非常优雅的解决方案,一旦它工作。
顺便说一下,我不同意应该把Spring注解添加到域对象中,而应该添加到各自的应用服务中。这只是一个旁注。