我正在开发一个简单的DDD +基于事件采购的应用程序,用于教育目的。
为了在存储到事件存储之前设置事件版本,我应该查询事件存储,但我的直觉告诉这是错误的,因为它会导致并发问题。
我错过了什么吗?
答案 0 :(得分:5)
根据您正在考虑的用例,有不同的答案。
通常,事件存储是一个愚蠢的,与域无关的设备。它表面上类似于List
抽象 - 它存储了你放在其中的内容,但它实际上并没有做任何工作来满足你的域约束。
在您的事件流只是已发生事件的持久记录的用例中(意思是:您的域模型没有得到否决权;记录事件不依赖于先前记录的事件),然后附加语义很好,根据您使用的设备类型,您可能不需要知道要写入的流中的哪个位置。
例如:GetEventStore的API理解ExpectedVersion.ANY
意味着将这些事件附加到流的末尾,无论它碰巧在哪里。
如果你关心以前的事件(域模型应该确保基于其先前状态的不变量),那么你需要做某事以确保你附加事件与您检查过的历史相同。最常见的实现方式是在流中传达写入游标的预期位置,以便设备可以拒绝写入错误位置的尝试(这可以防止并发修改)。
这并不一定意味着您需要查询事件存储以获取位置。您可以在加载时计算流中的事件数量,并记住您已添加的事件数量,以及流“#34;应该"如果你仍然与它同步。
我们在这里做的是类似于比较和交换操作:我们得到流的原始状态的表示,创建一个新的表示,然后比较并交换对原始的引用到而是指向我们的更改
oldState = stream.get()
newState = domainLogic(oldState)
stream.compareAndSwap(oldState, newState)
但是因为流是一个只附加语义的持久数据结构,所以我们可以使用一个不需要复制现有状态的简化API。
events = stream.get()
changes = domainLogic(events)
stream.appendAt(count(events), changes)
如果您的设备的API不允许您指定写入位置,那么是 - 当某个其他编写者更改您的查询之间的流的位置时,存在数据竞争的危险你的立场和尝试。在查询中获得的数据总是陈旧的;除非您持有锁定,否则在您阅读本地副本时,无法确定数据源是否已更改。
答案 1 :(得分:1)
我想你不应该考虑事件版本。
如果您在事件流中谈论该位置,通常情况下,无法保证在创建时确定它,仅在处理时间或事件存储中。
如果它与事件版本完全相同(请参阅http://cqrs.nu/Faq,如何对我的事件进行版本/升级?),您可以在应用程序中对其进行硬编码。所以,我的意思是下一个用例:
首先,您有一个应用程序生成一些事件。接下来,您更新应用程序和事件已更改(您添加一些字段或更改有效负载结构)但保持逻辑意义。所以,现在你的ES中的旧事件和新事件与旧事件有很大不同。要区分彼此,请使用事件版本,例如0和1。