使用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_;
}
答案 0 :(得分:7)
我不太关心命令消息,因为用户会立即知道它是否成功。
您可能应该关注命令消息,因为那些实际上会导致记录簿发生变化。是的,快乐的道路很简单,但是当用户的命令未被确认时,您希望用户做什么?
我关心的是事件。如果将聚合版本号附加到事件中,我们可以使操作具有幂等性吗?
请记住,事件来源的entites是从历史记录加载的,而不仅仅是飞过的事件(即订阅)。您将从记录簿中加载这些实体,因此您可以获得准确的历史记录。您不必担心事件发生变化,因为它们是不可变的。同样,您不必担心历史记录会发生变化,因为历史记录只会附加。
换句话说,您的事件源实体支持apply(Event)
方法,您根本不需要保护它,只需按顺序加载事件。
同一事物的另一种方式:您的实体的历史记录以DocumentMessage
的形式发送,而不是EventMessages
的序列。
对于根据多个事件历史汇总的预测,问题类似。
如果您的实体正在侦听/订阅其他实体事件(即您的实体是事件处理器),那么您将需要担心幂等性。请注意,在此用例中,事件有两种不同的上下文 - 事件处理器本身历史中的事件(即描述事件处理器状态更改的事件),以及处理器响应的事件。也就是说,您有apply(Event myStateChanged)
vs when(Event somethingHappenedSomewhereElse)
你不必担心前者的幂等性(见上文);通过在您自己的历史记录中跟踪该事件,确保您对事件做出反应一次。您的处理器可能是状态机(因此事件自然是幂等的),或者您可以跟踪您订阅的事件的事件ID,然后确保您不会反应多次等等。
奇怪的是,有些地方会出现“聚合版本” - 它们在命令处理程序中。两种常见形式:首先,命令可能针对要应用的聚合的特定版本(立即解决您的幂等问题);第二,你会看到跟踪版本号以防止并发修改。