使用事件源时,使用聚合版本号是幂等的

时间:2016-05-16 05:26:52

标签: domain-driven-design cqrs event-sourcing idempotent

使用DDD,CQRS和事件采购时,可能会重新发送或不按顺序发送消息。

我不太关心命令消息,因为用户会立即知道它是否成功。我关心的是事件。如果将聚合版本号附加到事件中,我们是否可以使操作具有幂等性? E.g:

class Person {
    public function apply(PersonNameUpdated event) {
        if (version_ + 1 != event.version) {
            name_ = event.name;
            ++version_;
        }
    }
    private String name_;
    private Integer version_;
}

1 个答案:

答案 0 :(得分:7)

  

我不太关心命令消息,因为用户会立即知道它是否成功。

您可能应该关注命令消息,因为那些实际上会导致记录簿发生变化。是的,快乐的道路很简单,但是当用户的命令未被确认时,您希望用户做什么?

  

我关心的是事件。如果将聚合版本号附加到事件中,我们可以使操作具有幂等性吗?

请记住,事件来源的entites是从历史记录加载的,而不仅仅是飞过的事件(即订阅)。您将从记录簿中加载这些实体,因此您可以获得准确的历史记录。您不必担心事件发生变化,因为它们是不可变的。同样,您不必担心历史记录会发生变化,因为历史记录只会附加。

换句话说,您的事件源实体支持apply(Event)方法,您根本不需要保护它,只需按顺序加载事件。

同一事物的另一种方式:您的实体的历史记录以DocumentMessage的形式发送,而不是EventMessages的序列。

对于根据多个事件历史汇总的预测,问题类似。

如果您的实体正在侦听/订阅其他实体事件(即您的实体是事件处理器),那么您将需要担心幂等性。请注意,在此用例中,事件有两种不同的上下文 - 事件处理器本身历史中的事件(即描述事件处理器状态更改的事件),以及处理器响应的事件。也就是说,您有apply(Event myStateChanged) vs when(Event somethingHappenedSomewhereElse)

你不必担心前者的幂等性(见上文);通过在您自己的历史记录中跟踪该事件,确保您对事件做出反应一次。您的处理器可能是状态机(因此事件自然是幂等的),或者您可以跟踪您订阅的事件的事件ID,然后确保您不会反应多次等等。

奇怪的是,有些地方会出现“聚合版本” - 它们在命令处理程序中。两种常见形式:首先,命令可能针对要应用的聚合的特定版本(立即解决您的幂等问题);第二,你会看到跟踪版本号以防止并发修改。