避免在事件源应用程序中保存BLOB数据的两阶段提交

时间:2017-10-12 13:14:23

标签: blob domain-driven-design

假设我们有一个汇总User,其中UserPortraitImageContract为PDF文件。我想将文件存储在专用的基于文档的存储中,并在事件中保存与进程相关的数据(带有BLOB数据的链接)。

但是,当我必须存储文件并存储新事件时,如何避免两阶段提交?

首先我会存储文件然后存储事件;如果第一个事务失败则无关紧要,命令失败。如果第二个事务失败,即使我们在商店中生成了一些死文件也没关系,命令失败;我们甚至可以应用回滚。 但是还有其他问题吗?

接下来的问题是如何设计聚合和事件。如果聚合只保存对BLOB存储的引用,那么调用SignUp命令后的进程是什么?

SignUpCommand ==>存储文档(UserPortraitImageContract)==>使用给定的BLOB存储引用创建新的User聚合并存储它?

是否有更好的设计可以减轻知道BLOB数据保存在另一个商店的总量?谁负责存储BLOB数据并将引用转发给聚合?

2 个答案:

答案 0 :(得分:3)

听起来你正在使用类似于AtomPub media-entry/media-link-entry对的东西。 blob将进入您的数据存储,元数据将被复制到聚合历史记录

  

但是,当我必须存储文件并存储新事件时,如何避免两阶段提交?

在实践中,你可能不会。

也就是说,如果blob存储和聚合存储碰巧是同一个数据库,那么您可以在同一个事务中更新它们。这两家商店结合在一起,并为您选择的存储添加了一些非常强大的限制,但这是可行的。

另一种可能性是你接受你所做的两个变化是彼此隔离的,因此在一段时间内两个商店彼此不一致。

在第二种情况下,the saga pattern正是您所寻找的,而且正是您所描述的;如果第二个操作失败,则将第一个操作与补偿操作配对。所以"手册"回滚。

或者不是 - 从某种意义上说,git对象数据库使用两阶段提交;一个对象被复制到对象存储中,然后树得到更新,然后提交...垃圾收集后来丢弃你不需要的对象。

  

谁负责存储BLOB数据并将引用转发给聚合?

嗯,最终这是一个基础设施问题;您的模型是否确实需要与文档进行交互,或者只是带有可以在以后兑换的claim check

答案 1 :(得分:1)

  

首先我会存储文件然后存储事件;如果是第一个   事务失败没关系,命令失败。如果是第二个   交易失败即使我们生成了一些也没关系   在商店中死文件,命令失败;我们甚至可以申请一个   回滚。但是还有其他问题吗?

除了浪费的磁盘空间,我不能想到。当我想避免分布式事务或者它们在两种类型的数据存储中不可用时,我通常会这样做。通常,两个操作中的一个不太重要,即使主操作稍后失败,您也可以让它完成。

在异常处理期间,可以通过@VoiceOfUnreason解释,在异常处理过程中或作为Saga的一部分来完成清理拙劣的尝试。

  

SignUpCommand ==>存储文件(UserPortraitImage和Contract)==>   使用给定的BLOB存储引用创建新的User聚合   存储它?

是。通常,应用程序层组件(在您的情况下为命令处理程序)充当不同数据存储之间的协调器,并在与另一个或与域通信之前从一个存储中获取所需的全部内容。