微服务:将一个必需的字段添加到一个有界上下文中,该上下文从一个单独的有界上下文获取数据



假设您有两个具有独立数据库的有界上下文(BC1和BC2(。

它们都包含一个用户实体,需要在有边界的上下文中保持同步。在这种情况下,假设我们通过集成事件来实现同步。

在本例中,用户实体"源自"BC1,这意味着总是向BC1发出创建/更新/删除用户的请求。对BC1中的用户执行的任何操作然后通过集成事件广播到BC2。BC1始终是广播者,BC2始终是与用户相关的集成事件的消费者。

此外,假设BC2的数据库最初被设计为在其用户实体中的字段比BC1的用户实体少。例如,BC2不需要用户。EmailAddress字段,该字段已从BC2的数据库架构中排除。

但是,需求的变化使得User。BC2中需要电子邮件地址。

在这种情况下,我们首先将此字段添加到BC2中的数据库模式中。但是,如果您已经处于生产环境中,则需要将此字段设置为可选字段(或为其指定默认值(。

下一步必须将电子邮件地址从BC1迁移到BC2中的相应用户。

您可能希望在BC2中只进行一次数据库迁移,添加一个可选的User。EmailAddress字段,从BC1的数据库中填充该字段,然后生成User。BC1中需要电子邮件地址。但这需要跨数据库查询,而这并不总是可能做到的

我的问题很简单,填充用户的最佳方式是什么。BC2中已有用户的EmailAddress字段?

当然,你可以写一个脚本来完成这项工作。但是,每当BC2中的User实体中添加了一个新字段时,你就必须更新脚本。

困难的根源在于BC1中的UserCreated集成事件已经被BC2使用,当这种情况发生时,BC2只是忽略了用户的电子邮件地址。您不能为了获得电子邮件地址而重播集成事件,因为集成事件只是响应域中的实际操作而广播的,而且从技术上讲,BC1的域中没有任何变化。

我正在考虑的一个选项是拥有"身份"域和集成事件。"identity"域事件可以按需发布,并且不会指示域中的任何更改。这只是一种将给定实体的状态广播到其他有界上下文的方式。也许这在技术上违反了DDD的一些原则,但它避免了每次在BC2中添加字段时都必须更新脚本。

但这需要跨数据库查询,而这并不总是可能做到的。

这实际上违背了有界上下文的思想。界限是不可跨越的,尤其是在持久性级别。这是因为绕过BC1的业务层,可能会违反整个系统中的业务约束。

当然,你可以写一个脚本来完成这项工作。但是,每当BC2中的User实体中添加了新字段时,你就必须更新脚本。

您不需要。将此操作视为一次性迁移,它将:

  1. 添加具有非null约束和空默认值的新列
  2. 更新BC2中的集成事件处理程序以包含电子邮件
  3. 迭代您的行并通过BC1中的查找进行填充

由于电子邮件只能在BC1中更改,因此更新是幂等的,因此您不关心并发性。你只需要运行一次,就完成了。其他的工作是由更新后的事件处理程序完成的。如果以后需要添加另一列,只需使用相同的操作将其作为新的迁移处理即可,无需更新任何内容。

"identity"域事件可以按需发布,不会指示域中的任何更改。

事件是系统状态的更改。如果域中没有更改,则称为查询。您的问题是使用CQRS模式执行每个服务的数据库的已知缺点之一:它需要最终的一致性,这需要复杂的数据迁移。

如果你需要做很多这样的列更改,你应该后退一步,确保你的有界上下文设计得很好(糟糕的设计会导致冗长的BC间通信(,或者如果你不应该考虑将你的体系结构切换到API组合。

最新更新