如何验证 DDD 中的事件流 - 事件溯源?

How to validate the event flow in DDD - Event sourcing?

我正在构建一个 DDD/CQRS 事件溯源应用程序。 (.NET、事件存储)

我读了很多关于它的文章,尤其是著名的银行账户主题。

提醒一下,我们有以下事件序列:

  1. 已创建银行账户
  2. 已存款
  3. WithDrawn
  4. 已更改所有者
  5. 已存款

但我从未找到一篇博客 post 解释如何验证事件序列? 我的意思是,如果我在 BankAccountCreated 之前先收到 Deposited 事件会怎样? 换句话说,如何检查银行帐户是否已创建? 我如何知道流处于有效状态?

我必须调用读取模型吗?每次?在每个事件中?聚合的每种方法? 如果用户发送了两次并且 readmodel 不同步会怎样?

我已经阅读了很多关于事件溯源的资料,也许还不够^^,但我没有找到任何有关事件流一致性的信息。

在我的应用程序中,如果 "first" 事件 (ContactAdded) 不存在,我将无法应用事件。 这是否意味着我每次需要做某事时都必须调用 EventStore?

感谢您的帮助。

那里有很多。

How do I know the stream is in a valid state?

每个流在每个事件上都应该有一个单调递增的版本号。每个流(聚合),事件 1 应先于事件 2 等。 EventStore 将通过应用乐观并发来确保这种级别的一致性。当您将事件写入事件存储时,您可以提供预期的流版本(例如最后写入的版本)。当您读取事件流时,您会获取最后一个版本号,并在将事件写入 EventStore 时将其传递。如果事件流自您上次阅读后增长,则会引发并发错误。

Do I have to call the read-model? every time? in each event? each method of the aggregate?

这里有些术语混淆。请记住事件与命令、读取与写入模型,以及每个模型的用途。您显示来自 Read 模型的数据。您根据您的写入模型进行验证和处理。

What happen if the user sent it twice and the readmodel is not sync, yet?

鉴于上面的乐观并发策略,您实际上得到了先入为主的策略。为了解决这个问题,您可以捕获并发错误并从头开始重新处理您的命令(从 EventStore 获取最新状态)。

Does it mean I have to call the EventStore each time I need to do something?

是的。在每个命令上,您将读取聚合和恢复状态的事件流。您可以使用快照作为优化,但概念保持不变。

How to validate the event flow in DDD - Event sourcing? How do I know the stream is in a valid state?

在领域驱动设计中,领域模型负责确保状态满足您的领域不变性。你可以把它想象成一个函数

everythingWeKnowNow = domainModel(everythingWeKnewBefore, newInformation)

随着新信息的到来,我们将其与之前发生的事情整合起来,以产生新版本的 "the truth"。

在 CQRS 世界中,这一切都在 "write model" 中发生 - 我们正在使用 "the truth" 的权威表示,而不是陈旧的副本,我们根据域名。

事件源部分实际上只是表示的不同:我们不是计算新状态,然后覆盖我们以前的副本,而是计算更改,并将这些更改附加到我们以前的副本(我们总是可以通过简单地重新计算状态枚举更改列表)。

因此我们的权威 状态副本始终包含领域模型按写入顺序计算的事件。

这里的广泛模式是模型是系统中信息的权威,事件溯源只是存储该信息的一种不同方式(支持时间查询)。

对于需要对事件进行权威排序的解决方案部分,诀窍是您从事件存储中检索它们作为有序序列,而不是通过读取它们一次并试图重建序列。换句话说,您将使用拉模型而不是推模型将事件从一个系统传送到另一个系统。

当您在基于推送模型的系统中工作时,事件可能被错误排序,那么您需要将其构建到您的设计中。这往往以跟踪丢失的信息并等待它到达的形式发生。

(您还必须更加注意 时间;消息到达时间是一个非常有损的替代品,无法知道事情发生的时间。

分布式系统困难,我们在一切都在本地时学到的捷径不再有效。