将新的 BC 引入 DDD 应用程序的最佳实践是什么?

What are the best practices to introduce a new BC to a DDD app?

这是一个关于在我们使用 ES 和 CQRS 以及 DDD 的系统中引入新 BC 的理论问题。所以就不举例了。

引入新的 BC-s 可能会产生有趣的问题,新的 BC-s 通过接收和发布域事件与旧的 BC-s 进行通信。这些问题的根源在于我们在事件存储中已经有了领域事件。当新的 BC 对那些旧的领域事件做出反应时,它会以一种不同步的方式来做到这一点 and/or 乱序。

例如,我们有一个旧的 BC A 并且我们引入了一个新的 BC B。两者都发布领域事件,我们称之为 ab。在新系统中,顺序很重要,例如 b1 必须始终在 a1 之后,但在 a2 之前。当事件存储中已经有了 a1a2a3 序列时,我们能做什么?我们应该在 a1 之后注入 b1 等等吗?这是一个巨大的事件存储的可行解决方案吗?将所有旧事件逐一重播并做出反应肯定需要很长时间。我们如何通过处理新创建的 b1 事件来防止向客户发送电子邮件,该事件对 5 年前的主题做出反应?有没有一种模式可以防止这类问题?

问题分析

The root of these problems that we already have domain events in the event storage.

如果您将新的 BC B 引入现有系统,这意味着 系统在没有 B 的情况下也能正常运行。上面的声明很清楚这一点,并产生以下后果:

  • B本应响应来自A的事件而产生的事件不需要发布。没有其他系统应该根据这些事件采取行动,因为它们是 人为的
  • 您可以随时选择与 B 一起直播。您事先唯一需要做的就是让 B 与系统的当前状态同步。

同步B

如果你相应地设计B,这并不难。

  1. 首先,您需要一个 重播模式 机制来将所有域事件导入 B 而无需发布来自 B 的事件作为响应. 如果您使用事件源,当然需要在内部保留 Bs 事件,但 不要发布它们。 此外,请确保 B 不会通过其他方式在重播模式下修改世界状态,例如不要发送电子邮件。
  2. 然后,将 B 切换到 实时模式 。现在 B 使用系统中的新事件并发布自己的事件。

您提到的事件排序问题仅在您对所有域事件使用统一事件存储并且还使用该存储从中发布事件时才会出现问题。如果是这种情况,那么您需要在回放阶段将 Bs 事件标记为 "internal",并在发布机制中隐藏它们。

注意:如果 B 是一个纯粹的反应式 BC(这可能是一个非常简单的 BC 的情况),那么您甚至不需要重放的东西。但大多数 BC 可能会这样做。

首先 DDD 不需要事件溯源。

we have an old BC A and we introduce a new BC B. Both publish domain events which we call a and b. In the new system the order matters for example b1 must always come after a1, but before a2.

事件可能是乱序的,即使在同一个组件(限界上下文)中也是如此。事务完整性仅在聚合内得到保证。

when we already have the a1, a2, a3 sequence in the event storage?

没关系。顺便说一句,除非您在 SERIALIZABLE isolation(或其特定于供应商的等效项)中工作,否则您对 SQL 数据库没有此保证。 Protip:它对性能的负担如此之重,以至于它在默认情况下从未启用;所以你没有使用它。

上面这部分要特别注意link:

Other transactions cannot insert new rows with key values that would fall in the range of keys read by any statements in the current transaction until the current transaction completes.

此外,虽然事件存储不应该有一个事件的多个副本,但事件(和其他消息,例如命令)可能会在组件之间多次到达。

Should we inject b1 after a1 and so on?

因为您的组件应该能够处理乱序(和重复事件)否

What can we do,

取决于用于集成组件的技术和消息的语义:

  • 如果您正在从 Web 服务、提要、数据库读取事件 table;这样它就永远不会消失;您也许可以忽略某个事件,直到它相关为止。

  • 同样地,您可以将事件放回它来自的消息队列,直到它相关为止。

  • 您可以使用称为 Saga/Process 经理的模式。

  • Is there a real race condition, at all?