1

在以战术域驱动设计为模型的事件源系统中,我无法处理以下情况:

  1. 项目被选择在发票上。
  2. 要么所有选定的项目形成发票,或者如果任何选定的项目无法分配,则不分配任何项目并且发票不存在。
  3. 不变性:一个项目不得出现在多张发票上。
  4. 一旦包含项目的发票存在,项目应转换为价格。

我的计划是

  • 发票聚合类型,因此发票有一个标识来分组项目并存储计算的价格。
  • 我会有一个 Item 聚合类型,它通过为每个聚合实例存储对一张发票的单个引用来跟踪不变量。

我想我需要针对新的 Invoice 聚合触发命令,其中包含要分配给新发票的现有项目 ID 的列表。这将发出一个关于发票创建的事件。

然后一些东西会监听这个事件并将它翻译成一个命令列表,这些命令将每个选定的项目分配给新发票。

我看到这可能会失败:例如,在发出命令后,其中一个选定的项目可能已分配给另一张发票。因此,我会以某种方式需要回滚所有未失败的分配并声明发票不再存在。

另一方面,要计算发票上的价格,我需要知道最初选择的所有项目何时实际分配给发票,以确保发票留在这里。

目前我正在使用 Elixir 中的 Commanded CQRS/Event Sourcing 框架,该框架基于 Erlang Actor 模型。

我的天真的想法来自于使用非分布式关系数据库的悠久历史,将整个情况放入一个同步事务中,该事务分布在两个聚合上。但是该框架似乎不支持这一点,它也或多或少地破坏了异步分布式聚合实现最终一致性的想法。

因此,我正在为我的问题寻找合适的解决方案。任何帮助,将不胜感激。

4

2 回答 2

1

我的天真的想法来自于使用非分布式关系数据库的悠久历史,将整个情况放入一个同步事务中,该事务分布在两个聚合上。但该框架似乎不支持这一点

是的,在一个事务中写入多个事件流通常不是好的做法。你还有几个选择:

检查然后创建:“顺序”同步一致性

由于我们讨论的是聚合创建,您可以按顺序以同步方式但没有事务的方式执行不变检查 (A) 和创建发票本身 (B)。由于发票在 B 完成之前不存在,因此不存在并发访问它的风险。因为 A 本身是原子的,所以您已经确定了所有可能的并发情况。在执行 B 之前,您只需检查 A 是否有任何问题。万一 B 失败,只需记录错误或发送通知即可。

  • 如果您可以在您的领域中找到合适的概念,请设计一个能够强制执行 A 的聚合 - 基本上它应该包含项目地图及其相应的发票。如果您设法回答“不变量在哪个范围内?(只有一个客户?一家公司?其他什么?)”这个问题并围绕该范围设计聚合,它通常效果很好。加载聚合,让它检查不变量,如果没问题,然后在其中注册新的项目/发票关联并生成新的 Invoice 聚合。

  • 以这样一种方式设计聚合,使您的持久存储在技术上能够强制执行唯一性不变量。例如,带有Item.Invoiced.(ItemId)作为键的事件流。这可以被认为是创建 Invoice 流的路径上的一个中间流。

创建并稍后检查:最终一致性

  • 在 上InvoiceCreated,尝试将新的项目/发票关联插入到具有(itemId)唯一约束的表中,或者使用上面提到的代理事件流。出于实际原因,该表可以与您的读取模型位于同一数据库中。如果插入失败,则触发补偿操作(从发票中删除项目、取消发票等)
于 2018-03-28T22:25:58.410 回答
1

在您的情况下,您需要使用saga作为模式。

您的 saga 示例流程可以是:

流量_1

  1. 创建了一个包含所有项目 id 和状态初始化的 Invoice 聚合,并输出多个事件——一个 InvoiceCreated,一个 InvoiceCreatedForItem
  2. 某人 (ProcessManager) 监听 InvoiceCreatedForItem 事件并尝试将项目分配给发票,然后使用 MarkItemSUccessfullyAddedToInvoice 命令更新发票。如果发生故障,则使用 MarkItemAdditionFailureToInvoice 更新 Invoice。

在所有 Item 的事件到来后,Invoice 可以选择发送 ItemSuccessfullyCreated 或处理其他失败情况。然后在 ITemSuccessfullyCretaed 之后将价格添加到 Invoice。

流 _ 2

  1. 创建了一个包含所有项目 ID 和已初始化状态的 Invoice 聚合并输出多个事件——一个 InvoiceCreated(包含所有项目 ID)
  2. 某人(ProcessManager)监听 InvoiceCreated 事件并创建另一个聚合或将 invoiceid 与项目与状态的列表放在某个表中。然后在事件中检查剩余的项目 ID 并将它们分配给发票并以与上述相同的方式更新发票,或者也可以更新价格。可能将其分解为多个对更好地管理代码更有意义。

您可能会想到其他流程。您可以继续阅读http://microservices.io/了解更多此类模式

于 2018-03-28T05:32:28.533 回答