在以战术域驱动设计为模型的事件源系统中,我无法处理以下情况:
我的计划是
我想我需要针对新的Invoice聚合发出一个命令,其中包含要分配给新发票的现有项目ID列表。然后,这将发出有关发票创建的事件。
然后会有人听取此事件并将其转换为命令列表,这些命令会将所选的每个项目分配给新发票。
我发现这可能会失败:例如,在发出命令后,其中一个选定项目可能已分配给另一个发票。因此,我会以某种方式需要回滚所有未失败的作业,并宣布发票不再存在。
另一方面,要计算发票上的价格,我需要知道最初选择的所有商品何时实际分配到发票,以确保发票留在这里。
目前,我正在使用基于Erlang演员模型的Elixir中的Commanded CQRS / Event Sourcing框架。
我的天真想法,源于与非分布式关系数据库一起工作的悠久历史,将整个情况放入分散在两个聚合上的同步事务中。但是框架似乎并不支持这一点,它或多或少地破坏了异步分布式聚合实现最终一致性的想法。
因此,我正在为我的问题寻找合适的解决方案。任何帮助将不胜感激。
答案 0 :(得分:1)
在您的情况下,您需要使用saga作为模式。
你的传奇的样本流程可以是:
FLOW _ 1
在所有项目发生事件后,Invoice可以选择发送ItemSuccessfullyCreated或处理其他失败案例。然后在ITemSuccessfullyCretaed之后,将价格添加到发票。
FLOW _ 2
您可以想到其他流量。您可以继续阅读http://microservices.io/了解更多此类模式
答案 1 :(得分:1)
我天真的想法,来自与我一起工作的悠久历史 非分布式关系数据库,就是整体而已 同情交易的情况蔓延到两者 聚集。但该框架似乎并不支持这个
是的,在一次交易中写入多个事件流通常不是好的做法。你还有几个选择:
检查然后创建:"顺序"同步一致性
由于我们正在谈论聚合创建,您可以按顺序,以同步方式执行不变检查(A)和本身发票创建(B)但不使用交易。由于发票在B完成之前不存在,因此不存在并发访问它的风险。因为A本身就是原子的,所以你已经找到了所有可能的并发案例。在做B之前,你必须检查A没有出现任何问题。万一B失败,只需记录错误或发送通知。
如果您可以在您的域中找到合适的概念,请设计一个能够强制执行A的聚合 - 基本上它应该包含项目地图及其相应的发票。如果你设法回答问题"它在哪个范围内保持不变,通常效果很好? (只有一个客户?一家公司?还有其他什么?)"并围绕该范围设计聚合。加载聚合,让它检查不变量,如果可以,则在其中注册新的项目/发票关联,并产生新的发票聚合。
设计聚合,使得持久存储在技术上能够强制执行唯一性不变量。例如,以Item.Invoiced.(ItemId)
为键的事件流。在创建发票流的路径上,这可以被视为interim stream。
稍后创建并检查:最终一致性
InvoiceCreated
上,尝试将新项目/发票关联插入到具有(itemId)
唯一约束的表中,或使用上面提到的代理事件流。出于实际原因,该表可以与您的读取模型位于同一数据库中。如果插入失败,则触发补偿操作(从发票中删除项目,取消发票等)