Answers:
您需要清楚地区分修改读取模型状态的事件与(可能)修改外部系统状态的事件。确保您没有任何“混合事件”一起修改两个状态。这样,您可以在特定的“重播模式”下重播事件,而不会再次触发外部系统的那些事件。在这种模式下,您还可以“模拟”任何最初由外部系统启动的事件(现在将其从重播队列中取出)。
不要忘记,重新部署步骤实际上意味着将读取模型的状态重置为较早的时间点。对于外部系统的状态,这可能是您无能为力的事情。
摘自Martin Fowler的活动采购文章:
事件源的基本思想是确保在事件对象中捕获对应用程序状态的每次更改,并且确保这些事件对象本身按照与应用程序状态本身相同的生命周期被应用的顺序存储。
因此,当您需要将系统状态恢复到某个时刻时,可以重播存储的状态,而不是事件处理程序,直到那一刻。
话虽如此,如果您只使用状态数据,那么对外部系统不会有任何影响。除非事件存储上有触发器或监视程序,否则在还原期间应禁用它们。由于您说您无法控制外部系统,因此不应尝试使用公开的API来恢复其状态,因为您不知道外部系统可能会对系统产生什么副作用。如果还原使系统处于中间状态(例如,由于外部系统中的失败操作),则这不应属于事件重播的职责。
但是,如果其中一个事件会导致不在您控制范围内的外部系统将商品“运送”给客户,而您只是重播事件,则该商品将被运送两次。
为了选择一个具体的例子,让我们考虑一下“至少一次”副作用的工作方式。
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()
}
这里的要点是