Transactions 使用JOliver EventStore更新多个聚合

Transactions 使用JOliver EventStore更新多个聚合,transactions,aggregate,cqrs,neventstore,Transactions,Aggregate,Cqrs,Neventstore,我有一个关于在一个事务中使用更新多个聚合的问题。据我所知,每个聚合都应该有自己的事件流。现在,虽然许多命令处理程序将只加载单个聚合并只更新该聚合(即为这些聚合保存事件),但我可以想象,将有一些命令处理程序需要更新多个聚合。当然,我想用一种交易的方式来做 但是,我不知道如何使用事件存储实现这一点。通过调用事件流上的CommitChanges()来存储事件。如果我们要更新多个聚合,那么将有多个事件流,从而有多个对CommitChanges()的调用。实现该事务性的唯一方法是将其包装在Transact

我有一个关于在一个事务中使用更新多个聚合的问题。据我所知,每个聚合都应该有自己的事件流。现在,虽然许多命令处理程序将只加载单个聚合并只更新该聚合(即为这些聚合保存事件),但我可以想象,将有一些命令处理程序需要更新多个聚合。当然,我想用一种交易的方式来做

但是,我不知道如何使用事件存储实现这一点。通过调用事件流上的
CommitChanges()
来存储事件。如果我们要更新多个聚合,那么将有多个事件流,从而有多个对
CommitChanges()
的调用。实现该事务性的唯一方法是将其包装在
TransactionScope
中,但这没有多大意义,因为底层存储技术可能不支持事务。所以我最终得到了这段代码,这绝对不是我想要的:

        Guid aggregateGuid1 = Guid.NewGuid();
        Guid aggregateGuid2 = Guid.NewGuid();
        Guid commitGuid = Guid.NewGuid();

        var stream = store.OpenStream(aggregateGuid1, 0, int.MaxValue);
        stream.Add(new EventMessage() { Body = new MonitorDisabled { MonitorGuid = aggregateGuid1, User = "A" } });
        stream.CommitChanges(commitGuid);

        stream = store.OpenStream(aggregateGuid2, 0, int.MaxValue);
        stream.Add(new EventMessage() { Body = new MonitorEnabled { MonitorGuid = aggregateGuid2, User = "B" } });
        // Can't commit twice with the same commit id, what if fails after first one? No way for the store to know it had to write the second part of the commit.
        stream.CommitChanges(commitGuid);

这让我觉得我完全忽略了事件存储应该如何使用。有人能帮我吗?非常感谢

我不能代表约翰·奥利弗说话,但我认为答案是“不要”。聚合是事务边界。如果需要协调多个聚合的提交,则需要一个明确的协调过程(如saga),该过程可以完成,必要时可以撤消相关事件

聚合定义了事务边界

如果您需要执行交叉聚合事务,您应该检查聚合并重新设计它们


如果一个操作(命令)影响多个聚合,并且您确信您的聚合设计良好,并且映射到域中的实际一致性边界,则可能是您需要的。只需向每个聚合发送一个命令,并有两个事务,每个事务一个。如果你觉得最终的一致性不适合你的情况,恐怕就要重新开始了

有时,如果您的基础架构支持分布式事务,那么在这些场景中可以更容易地利用分布式事务

事务处理处理与事件存储:

默认情况下,EventStore禁止创建任何环境事务 在将更改提交到数据库之前由NServiceBus执行。然而如果 您正在使用一个队列和一个支持分布式的数据库 事务(MSMQ、SQL Server、Raven等),然后您可以更改 EventStore的TransactionScope选项为必填项。这将确保 EventStore在环境事务中登记,其中 使用MSDTC、消息队列和数据库进行分发 将保持同步。 -

与RavenDB的跨文档交易:

你会从我枯死、冰冷、破碎的手上撬开交易 -


现在,假设我们从事托管业务,管理数据中心机架中房间中的服务器。每个服务器都由一组监视器监控,我们称之为监视器配置。现在,我们想在维护期间禁用监视器。而且,我们可以对服务器、机架、房间或完整的数据中心进行维护。监视器配置是服务器监视器的自然聚合,我们希望在维护机架时在监视器配置聚合上引发MonitorDisabled事件。我们在哪里定义TX边界?服务器、机架、房间……如果你有跨越聚合边界的转录边界,你就错了。根据定义,聚合是交易边界,你的模型应该反映这一点。Hmm,让我们举一个典型的例子:银行交易。一个帐户是一个集合,我必须把钱从一个帐户转到另一个帐户。这意味着我必须在一个帐户上存储事件,如MoneySent,在另一个帐户上存储EventMoneyReceived。这对于我来说是2个聚合上事务性更新的合法场景。我遗漏了什么?更多的阅读告诉我这个案例应该使用补偿动作来实现。我还得多读一些这方面的书。能够在2个聚合上编写一个事务似乎是一个更干净的解决方案(当您控制域中的两个帐户时)。使用DDD和ES/CQR,您将获得一些东西,也将失去一些东西。这是您获得分区、可伸缩性、高可用性等的关键,但您失去了一致性。其实你并没有把它弄丢,你只是把它耽搁了。假设收益超过了损失,那么您就需要为您描述的案例找到解决方案,正如你已经发现的,通常有一些被普遍接受的解决方案,比如补偿措施。另一件事是,你最好确保得不偿失:构建一个可供不到100人使用的高度可扩展的系统是毫无意义的。