重播事件时如何处理CRQS中的副作用?


10

据说在CQRS中,修复错误很容易,您只需重新部署然后重播事件即可。

但是,如果其中一个事件会导致不在您控制范围内的外部系统将商品“运送”给客户,而您只是重播事件,则该商品将被运送两次。

您如何解决?

Answers:


6

您需要清楚地区分修改读取模型状态的事件与(可能)修改外部系统状态的事件。确保您没有任何“混合事件”一起修改两个状态。这样,您可以在特定的“重播模式”下重播事件,而不会再次触发外部系统的那些事件。在这种模式下,您还可以“模拟”任何最初由外部系统启动的事件(现在将其从重播队列中取出)。

不要忘记,重新部署步骤实际上意味着将读取模型的状态重置为较早的时间点。对于外部系统的状态,这可能是您无能为力的事情。


“别忘了,重新部署步骤实际上意味着将读取模型的状态重置为一个较早的时间点。对于外部系统的状态,这可能是您无能为力的事情。” ->但是,如果我希望重播重试失败的外部系统调用(如运送)怎么办?在这种情况下,我重新部署重播不仅会重置读取模型的状态,还会导致外部事件,这听起来是否合理,或者我缺少某些东西?
Jas

2
@Jas:您不想滥用“重播”重试失败的外部系统调用。您可以使用“重播”使自己的系统的读取模型处于与以前相同的状态。这意味着,如果发运请求失败,则会在此失败之前通知您的系统,并将该信息存储在其状态中。重播确保此信息在“重新部署和重播”之后仍然存在。因此,在重播之后,您的系统可能会应用“发生故障时重试运送”策略(与CQRS无关,任何健壮的订购系统都应该具有这种策略)。
布朗

有趣的是,这是我的初衷,只是想知道是否有“样式”,所以我不会重新发明轮子!
Jas

3

摘自Martin Fowler的活动采购文章:

事件源的基本思想是确保在事件对象中捕获对应用程序状态的每次更改,并且确保这些事件对象本身按照与应用程序状态本身相同的生命周期被应用的顺序存储。

因此,当您需要将系统状态恢复到某个时刻时,可以重播存储的状态,而不是事件处理程序,直到那一刻。

话虽如此,如果您只使用状态数据,那么对外部系统不会有任何影响。除非事件存储上有触发器或监视程序,否则在还原期间应禁用它们。由于您说您无法控制外部系统,因此不应尝试使用公开的API来恢复其状态,因为您不知道外部系统可能会对系统产生什么副作用。如果还原使系统处于中间状态(例如,由于外部系统中的失败操作),则这不应属于事件重播的职责。


2

但是,如果其中一个事件会导致不在您控制范围内的外部系统将商品“运送”给客户,而您只是重播事件,则该商品将被运送两次。

为了选择一个具体的例子,让我们考虑一下“至少一次”副作用的工作方式。

State currentState = State.InitialState
for(Event e : events) {
    currentState = currentState.apply(e)
}
for(SideEffect s : currentState.querySideEffects()) {
    performSideEffect(s)

因此,领域模型会跟踪需要完成的工作;但是将实际工作留给应用程序

在运行命令的上下文中,基本思想看起来是相同的。实际的副作用发生更新模型的事务之外

因此,模型的单元测试可能类似于

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced)

    // Then
    List.of(SendEmail) === currentState.applyAll(events).querySideEffects()
}

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced, EmailSent)

    // Then
    List.EMPTY === currentState.applyAll(events).querySideEffects()
}

这里的要点是

  • 更新模型没有副作用。实际的副作用发生在更新模型的事务之外。
  • 描述副作用结果的事件需要回来。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.