[英]Logic exception in a buggy incoherent stream of events in DDD, CQRS, EventSourcing?
假设您使用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点开始,那里是仓库的进口门。 无论这个例子在一天结束时叉车如何消失,都不在乎。
这些命令可以是:
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
,事件8
向移动C
。
但是,如果序列继续这样下去怎么办?
# TimeStamp Event
----------------------------------------------
[...]
6 23/jul 09:00 started
7 23/jul 10:00 wentRight
8 23/jul 13:00 crossed
9 23/jul 15:00 wentUp
10 23/jul 16:00 wentRight
11 27/jul 09:00 started
12 27/jul 11:00 wentDown
某个领域专家问您:“嘿,极客,您告诉我们事件外包是不可思议的:7月23日18:00的叉车在哪里?”
我们都知道电梯不能“跳上楼梯”,所以我们都知道事件9永远不会发生。
因此,我们的“重播器”无法做其他抛出异常的事情。 但是已经写入的事件序列就是那个。
这里的主题不是“如何编写一个好的序列”,而是“面对带有异常的序列时该怎么办”。
如果编写事件流的软件包含违反域逻辑的错误,如何“重放”事件流?
您至少有两个选择:
apply
方法或事件订阅者中添加一些修复代码(Readmodels,projections,Sagas); 这段代码应该处理您要避免的确切情况。 它的缺点是它将永久存在于代码库中,但是它的优点是可以在零停机时间内完成。
这样做的缺点是,替换事件存储区时可能需要一些停机时间,但是这样做的好处是,您可以“忘记”错误,从而获得干净/正确的事件流。
你会写一个补偿事件吗? 怎么样? 哪一个? 什么时候?
当您需要快速解决方案时,编写补偿事件非常方便。 这是解决方案编号的特例。 1。
您会违反黄金法则并“触摸”历史吗? 实际上,这不是“历史”,因为事件“ 5”并未真正发生。 那我们可以触摸吗?
您一定可以这样做,因为我确实想获得最快的解决方案,但是根据框架/技术的不同,它可能很难看。 例如,订阅者无法再确定他们是否处理了事件存储中的所有相关事件,因此,为了确定,您需要重建所有Readmodels; Sagas可能会遇到的最大问题是,处理事件会产生副作用。
关于法律方面,如果您确实修改了历史记录,则需要存档旧的事件流,以防万一有人要求它。 这取决于您的域。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.