使用 JOliver EventStore 更新多个聚合

发布于 2024-12-15 14:27:13 字数 1255 浏览 5 评论 0原文

我对使用 JOliver 的事件存储在单个事务中更新多个聚合有疑问。据我了解,每个聚合都应该有自己的事件流。现在,虽然许多命令处理程序将仅加载单个聚合并仅更新该聚合(即保存这些聚合的事件),但我可以想象将会有命令处理程序需要更新多个聚合。当然,我想以交易的方式做到这一点。

但是,我不知道如何使用事件存储来做到这一点。存储事件是通过在事件流上调用 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);

这让我觉得我完全错过了应该如何使用事件存储的一些东西。有人可以帮我吗?多谢!

I'm having a question regarding updates to multiple aggregates in a single transaction using JOliver's Event Store. As I understand, every aggregate should have its own event stream. Now, while many command handlers will only load a single aggregate and only update that aggregate (i.e. save events for those aggregates), I can imagine that there will be command handlers which need to update multiple aggregates. And of course, I would like to do that in a transactional way.

However, I don't see how I could do that with the Event Store. Storing events is done by calling CommitChanges() on an event stream. If we're having multiple aggregates to update, will have multiple event streams and thus multiple calls to CommitChanges(). The only way to make that transactional is to wrap it in a TransactionScope, but that does not make much sense, since the underlying storage technology might not support transactions. So I end up with this code, which is definitely not what I am looking for:

        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);

This makes me feel I'm completely missing something on how the Event Store should be used. Could anybody help me out here? Thanks a lot!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最美不过初阳 2024-12-22 14:27:13

聚合定义事务边界。

如果您需要执行跨聚合事务,您应该检查您的聚合,并可能重新设计它们。

如果一项操作(命令)影响多个聚合,并且您确信聚合设计良好并映射到域中的真实一致性边界,最终一致性可能就是您正在寻找的。只需向每个聚合发送一个命令,并有两个事务,每个事务一个。如果您觉得最终一致性不适合您的情况,那么恐怕它又回到了绘图板。

An Aggregate defines a transaction boundary.

If you need to perform cross-aggregate transactions you should review your aggregates and maybe redesign them.

In cases where an operation ( command ) affects more than one aggregate, and you are sure that your aggregates are well design and map to real consistency boundaries in your domain, eventual consitency might be what you are looking for. Just send a command to each aggregate, and have two transactions , one for each of them. If you don't feel eventual consistency is right for your case than i'm afraid it's back to the drawing board.

最美的太阳 2024-12-22 14:27:13

我不能代表约翰·奥利弗,但我认为答案是“不要”。聚合是事务边界。如果您需要协调多个聚合的提交,则需要一个显式的协调过程(例如传奇),它将执行并在必要时撤消相关事件。

I can't speak for John Oliver, but I think the answer is "don't". Aggregates are transaction boundaries. If you need to coordinate the commit of several aggregates, you need an explicit coordination process, like a saga, that will do and if necessary undo the relevant events.

梦中的蝴蝶 2024-12-22 14:27:13

有时,如果您的基础设施支持分布式事务,那么在这些场景中可以更轻松地利用分布式事务。

TransactionScope 与 EventStore:

默认情况下,EventStore 会抑制创建的任何环境事务
在向数据库提交更改之前由 NServiceBus 执行。然而如果
您正在使用支持分布式的队列和数据库
事务(MSMQ、SQL Server、Raven 等),那么您可以更改
EventStore 的 TransactionScopeOption 为必需。这将确保
EventStore 加入环境事务,其中将
使用 MSDTC、消息队列和数据库进行分布式
将保持同步。
- NES 文档

使用 RavenDB 进行跨文档事务:

你会从我那双死去、冰冷、破碎的手中窃取交易信息
- 艾延德< /p>

Sometimes it can be easier to take advantage of distributed transactions in these scenarios if your infrastructure supports them.

TransactionScope with EventStore:

The EventStore by default suppresses any ambient transaction created
by NServiceBus before committing changes to the database. However if
you are using a queue and a database that support distributed
transactions (MSMQ, SQL Server, Raven etc) then you can change the
EventStore's TransactionScopeOption to Required. This will ensure that
the EventStore enlists in the ambient transaction, of which will
become distributed using the MSDTC, and the message queue and database
will be kept in-sync.
- NES Documentation

Cross document transactions with RavenDB:

You’ll pry transactions from my dead, cold, broken hands
- Ayende

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文