假设您通过EventSourcing处理DDD。
我们都知道事件是不可变的,因此永远不要从事件日志中删除它们。但是,如果流在逻辑上是“不正确的”怎么办?不是那种经典的情况:“我加钱了,我不必加钱,所以创建一个补偿事件来取钱。”
我不是在谈论运行时异常,而是在事件流中发现的逻辑异常,因为编码人员在事件编写器中犯了错误。
如果编写事件流的软件包含违反域逻辑的错误,您如何“重播”事件流?
Oookay ...我们都知道,“ 应该从未发生过”和“解雇编写这些事件编写器的编码器”等等……
但是我们假设事件流 就在那儿,并且您正在重建所有流的投影重放。只是可能已经发生,然后告诉您从现有事件流中重建预测。
突然,当重放事件流时,您会发现不符合当前业务规则或当时存在的规则的“不连贯”事件。
您有以下事件:
# TimeStamp Event Data
------------------------------------------------------
1 03/jul car.created { id: 4444, color: blue }
2 14/jul car.delivered { id: 4444, to: Alice }
3 18/jul car.created { id: 5555, color: blue }
4 22/jul car.created { id: 5566, color: orange }
5 25/jul car.created { id: 5577, color: blue }
7月26日,有人问:“您有多少辆蓝色汽车?”
透明晶体:2个单位(标识5555
和5577
)。
原因:单位4444
已售出。单位5566
为橙色。
但是,如果您有这个越野车序列怎么办?
# TimeStamp Event Data
------------------------------------------------------
1 03/jul car.created { id: 4444, color: blue }
2 14/jul car.delivered { id: 4444, to: Alice }
3 18/jul car.created { id: 5555, color: blue }
4 22/jul car.created { id: 5566, color: orange }
5 23/jul car.created { id: 5555, color: red }
6 25/jul car.created { id: 5577, color: blue }
当然,事件5永远不会发生,您不能创建相同的单元2次。
调查领域专家后,您发现事件5不正确。它应该显示为“ car.repainted”,但该软件存在错误,并编写了“ car.created”。
让我们假设一个仓库里有叉车来从架子上取东西。仓库包含2条垂直走廊,2条水平走廊和1条对角走廊。
所有走廊都是双向的,除了左垂直的走廊上有某种台阶或类似的东西外,叉车只能从A移到C,而不能反向移动。并且从下面的水平线也有台阶,并且叉车只能从D移至C,而不能从C移至D。
购买机器后,每天从仓库A的进口门开始在A点。无论这个例子在一天结束时叉车如何消失,都不在乎。
命令可以是:
purchase()
start()
goRight()
goLeft()
goUp()
goDown()
cross()
事件可以是:
purchased
started
wentRight
wentLeft
wentUp
wentDown
crossed
这是叉车集合体的可能状态图:
让我们假设您正在重放聚合的事件,并且发现了这些事件:
# TimeStamp Event
----------------------------------------------
1 12/jul 10:00 purchased
2 14/jul 09:00 started
3 14/jul 11:00 wentDown
4 14/jul 12:00 crossed
5 14/jul 14:00 wentDown
6 23/jul 09:00 started
7 23/jul 10:00 wentRight
8 23/jul 13:00 crossed
有人问“现在叉车在哪里?您可以轻松地告诉“ C”。
原因:无论6
之前发生了什么,因为事件6
重置为位置A
,事件7
都移向B
,事件{{1} }向8
移动。
但是,如果序列继续这样下去怎么办?
C
一些领域专家问您:“嘿,极客,您告诉我们事件外包非常神奇:7月23日18:00的叉车在哪里?”
我们都知道电梯不能在楼梯上“跳跃”,所以我们都知道事件9永远不会发生。
因此,我们的“重播器”无法做其他引发异常的事情。但是已经编写的事件序列就是那个。
这里的主题不是“如何编写一个好的序列”,而是“面对异常序列时该怎么做”。
答案 0 :(得分:2)
如果编写事件流的软件包含违反域逻辑的错误,您如何“重播”事件流?
您至少有两个选择:
apply
方法或事件订阅者(Readmodels,projections,Sagas)中放入一些修复代码;这段代码应该处理您要避免的确切情况。 它的缺点是它将永远存在于代码库中,但是它的优点是可以在零停机时间内完成。
它的缺点是替换事件存储区时可能需要停机,但是这样做的好处是您可以“忘记”错误,从而获得干净/正确的事件流。
您会写一个补偿事件吗?怎么样?哪一个?什么时候?
想要快速解决方案时,编写补偿事件很方便;这是解决方案编号的特例。 1。
您会违反黄金法则并“触摸”历史吗?实际上,这不是“历史”,因为事件“ 5”并未真正发生。那我们可以触摸吗?
您可以做到这一点,我确实做到了,因为我想要尽可能最快的解决方案,但是根据框架/技术的不同,它可能很难看。例如,订阅者无法再确定他们是否处理了事件存储中的所有相关事件,因此,为了确定,您需要重建所有Readmodels; Sagas可能会遇到的最大问题是,处理事件会产生副作用。
关于法律方面,如果您确实修改了历史记录,则需要存档旧的事件流,以防万一有人要它。这取决于您的域。