DDD,佐贺(Saga)和事件来源:可以将补偿行为简单地删除事件存储区吗?


15

我意识到上述问题可能会引起一些“什么?”,但让我尝试解释一下:

我试图将一些相关概念,基本上是Saga模式(http://www.rgoarchitects.com/Files/SOAPatterns/Saga.pdf)与事件源(DDD概念)结合起来:http : //en.wikipedia.org/wiki/Domain-driven_design

一个很好的文章,将其包装在一起: https //blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-ii-of-ii/

我一分钟就会解决这个问题,但是我想我必须先总结一下我对它的理解(这很可能是错误的,所以请纠正这种情况),因为这很可能会影响我为什么提出以下问题:

  1. Saga模式是一种代理,它给定操作(最终用户,自动化等,本质上是要更改数据的任何操作),将该操作划分为业务活动,并将这些活动中的每一个作为消息发送给Message Bus,依次将其发送到各个聚合根进行处理。
  2. 这些聚集的根可以完全自主地运行(关注点分离得很好,可扩展性强等)。
  3. Saga实例本身不包含任何业务逻辑,该逻辑包含在向其发送活动的聚合根中。Saga中包含的唯一“逻辑”是“过程”逻辑(通常实现为状态机),它根据收到的动作(以及后续事件)确定要做什么(即:发送什么活动)
  4. Saga模式实现了一种分布式事务模式。即:当聚合根之一(可以再次自动工作,而又不了解彼此的存在)失败时,可能必须回滚整个操作。
  5. 这是通过让所有聚合根实现的,将它们的活动报告完成后返回给Saga。(无论成功还是失败)
  6. 如果所有聚合根均返回成功,则内部状态机是否由Saga决定下一步(或决定已完成)
  7. 在失败的情况下,Saga将参与最后一个动作的所有聚合根发出一个所谓的补偿动作,即:撤销每个聚合根所做的最后一个动作的动作。
  8. 如果操作是“加1票”,则可能只是在进行“减1票”,但可能会更复杂,例如将博客文章还原到以前的版本。
  9. 事件源(请参阅结合了这两个博客文章)旨在将保存在外部的每个汇总根源的每个活动的结果保存到集中式事件存储中(在这种情况下,这些更改称为“事件”)
  10. 此事件存储区是“事实的单一版本”,可用于简单地通过迭代存储的事件来重播所有实体的状态(本质上类似于事件日志)
  11. 将两者结合起来(即:让聚集的根使用事件源来将其更改外包,然后再将其报告回Saga)可以实现很多不错的可能性,其中之一是我的问题...

我觉得我需要把这件事从肩膀上移开,因为一口气要掌握很多。鉴于此上下文/心态(再次,请纠正错误)

问题:当汇总根接收到补偿动作并且如果该汇总根已使用事件源外包将其状态更改外包时,补偿动作不是在所有情况下都只是删除事件存储中针对该事件的最后一个事件给定总根?(假设持久实现允许删除)

这对我来说很有意义(这将是这种组合的另一个很大的好处),但是正如我所说的那样,我可能基于对这些概念的错误/不完全理解而做出这些假设。

我希望这不会太冗长。

谢谢。

Answers:


9

您不应该从事件存储中删除事件。这些代表发生的事情。如果现在发生了某些事情,那么您也应该知道这一点,因此添加一个事件,该事件将迫使您的聚合返回到以前的状态。很遗憾从数据库中删除信息。

想想格雷格·杨(Greg Young)的购物车示例和已移除的物品。可能是明天的补偿行动信息可能具有任何价值。

至于传奇,据我所知,每件事都是正确的。您应该将传奇视为状态机。它只是这样做。

这是一个传奇发布的命令,告诉聚合器执行某项操作。这个动作将会有一个事件。此事件可能是您需要的纠正措施,或者可能需要在视图中取消规范化,以便某些用户可以看到它来决定要做什么。

这取决于您的业务规则。要么有一个简单的解决方法:聚合根知道在这种情况下,您可以这样做,或者选择太复杂而无法以编程方式进行(开发成本太高),因此,您可以将此建议提供给某些用户,可以在不同的动作之间进行选择。一切都取决于补偿行动的背景。这是纯粹的业务规则。在这种情况下我们该怎么办。这是要问您的域专家,然后与他一起选择,是开发自动解决方案更好还是该解决方案是询问用户Ronald R.,因为他通常是回答此问题的人。


聚合将如何知道要还原到哪个状态?是否可以对事件存储进行查询?
杰尔特·扬

它不应该还原而是添加另一个事件。传奇发布的这是一个命令,告诉聚合执行某项操作。这个动作将会有一个事件。
Arthis

是的,我明白。“还原”可能是错误的选择。请考虑以下因素:我可能想将Event Sourcing与Sagas一起用于建模,其中包括文档发布/批准流程。如果编辑者已将一个ChangeBlogBody-action最终作为BlogBodyChanged-event持久保存到事件存储,该怎么办。此后,出于某种原因,将发出补偿措施。与发布ChangeBlogBody-action之前一样,聚合人如何知道触发什么补偿事件导致Blog主体的内容?
杰尔特·扬

这取决于您的业务规则。要么,有一个简单的解决方法:聚合根知道在这种情况下,您可以这样做,或者选择太复杂而无法以编程方式进行(开发成本太高),因此,您可以将此建议提供给某些用户,可以在不同的动作之间进行选择。
Arthis 2012年

一切都取决于补偿行动的背景。这是纯粹的业务规则。在这种情况下我们该怎么办。这是要问您的域专家,然后与他一起选择,是开发自动解决方案更好还是该解决方案是询问用户Ronald R.,因为他通常是回答此问题的人。
Arthis 2012年

6

为了完整起见,我想包括Martin Fowler的有关恢复状态的方法的摘要:

除了向前推进的事件外,使他们能够逆转自己通常也很有用。

当事件以差异的形式进行时,逆转是最直接的。例如,将“向马丁的帐户添加10美元”,而不是“将马丁的帐户设置为110美元”。在前一种情况下,我可以减去10美元来冲销,但在后一种情况下,我没有足够的信息来重新创建帐户的过去值。

如果输入事件未遵循差异方法,则事件应确保其存储了处理过程中冲销所需的所有内容。您可以通过将先前的值存储在任何已更改的值上,或者通过计算并存储事件的差异来做到这一点。

来自:http : //martinfowler.com/eaaDev/EventSourcing.html


1

从概念上讲:

  • 事件是发生了什么事。过去不能改变。
  • 命令是要做的事。命令可能不会发生(可能会被拒绝)。

我们只能通过执行另一个命令(补偿动作)来更改将来的状态,这将导致事件更改应用程序的状态。

要“混淆”该问题,请考虑以下短语“删除最后一个事件”:

  • 如果最后一个事件不是必须删除的事件怎么办?如果要删除的事件之后发生了其他事件怎么办?这些事件也必须更改(因为它们的基本状态将通过删除它们之前的事件而更改)。
  • 如果事件发送到外部系统怎么办?除了发送另一个事件之外,您可能无权更正其状态。

简而言之,您无权删除CQRS模式中的事件。

您可以通过创建快照状态(基于导致该状态的所有事件)来缩小事件存储,但是此物理方面与事件的概念无关。


0

吉尔特·简(Geert-Jan),我也认为赔偿行动可以简单地删除相应的事件。这很有意义,并且显示了事件源设计模式的另一个好处:补偿事务设计模式的实现更加容易。

有人说删除事件违反了事件来源或CQRS的“原则”。我认为这是一个局限性的概括。我认为,如果事件是在已取消的全局事务范围内创建的,则可以删除该事件。考虑伪代码:

try {
    newEvents = processMycommand(myCommand)
    for (Event newEvent : newEvents)
        EventStore.insertEvent(newEvent);
} catch (Exception e) {
    EventStore.rollback();
}

假设您的事件存储是一个事务性数据库。在伪代码中,您可以想象插入第一个事件的情况,但是当尝试插入第二个事件时,抛出了Exception。rollback命令自然会恢复第一个事件的插入。那合理吗?

如果对于ACID数据库事务(通过提交/回滚进行事务处理)合理,为什么对补偿事务不合理?

执行全局Saga事务时,可以撤消数据更改(通过补偿)。由于事务未完成,因此无需保留在事务期间创建的事件。

现在,如果补偿尝试删除一个事件,而该事件不是对象上的最新事件,则不应发生删除。但是总的来说,这不太可能发生,尤其是在读取密集型解决方案中。


0

让我们考虑一下,事件存储只是您要保存的数据。例如,我们可以有一个硬盘驱动器,我们可以从头开始并在其上写入数据。每次发生事件时,我们都会附加先前的数据,因此我们将继续在上次停止的磁盘上继续写入。当我们想删除一个事件时,我们回去,从磁盘上删除该部分,并留一个空隙。自行移回会减慢事件存储的速度,但是我们可以接受。如果使用文件系统,它将尝试用后面的事件来填补空白,因此我们最终将拥有缓慢的碎片存储空间,或者可以进行碎片整理。他们俩都不是我们喜欢的东西。如果我们在谈论数据库,那么如果您使用关系数据库而不是仅附加数据库来存储事件,则会得到以下信息:

在此处输入图片说明 参考:https : //cambridge-intelligence.com/bringing-time-series-data-to-life-with-keylines/

Ofc。对于普通网站而言,这并不重要,但是这些解决方案是为大型网站(如Facebook,Google等)设计的。因此,实际上这个问题没有任何意义,因为您如何删除仅附加数据库中的事件,以及如何删除您称补偿为某种东西,例如建造一台时光机并及时移回以更改或阻止某个事件?

据我所知。解决此问题的唯一方法是创建一个新的事件存储,其中排除了不需要的事件,然后删除了旧的事件存储。但这是删除单个事件的极端解决方案。如果我们谈论的是GDPR,那么我所知道的唯一相对好的解决方案是将加密的个人数据存储在事件存储中,并从其他数据库中删除加密密钥。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.