读取端数据库与事件处理程序的最终一致性。如何分段读取端更新

Read-Side DB eventual Consistency with Event Handlers. How to segment read side updates

我已经实现了一个事件存储系统,其中我的根聚合是在仓库之后建模的。例如,我有一些事件,例如 BoxCreatedBoxLocationChanged。这两个事件都有单独的事件处理程序来更新读取端 table dbo.Boxes

但是,我现在已经到了这两个处理程序都需要更新另一个读取端数据库 dbo.BoxType 的地步,该数据库跟踪每个盒子类型的库存状态和位置。

我的问题是我是否应该为这些新的读取端更新创建一个单独的事件处理程序(使用相同的事件),或者我是否应该将另一个存储库注入当前事件处理程序并处理所有读取端更新相同的事件处理程序?

首选的设计选择是什么?选择是否取决于我未列出的其他因素?

为了演示,使用 MediatR,我目前有如下所示的事件处理程序:

public class BoxCreatedHandler : INotificationHandler<BoxCreated>
{
    private readonly IBoxRepository _repo;

    public BoxCreatedHandler(IBoxRepository repo)
    {
        _repo = repo;
    }

    public async Task Handle(BoxCreated e, CancellationToken ct)
    {
        Box b = JsonConvert.DeserializeObject<Box>(e.Data);
        _repo.CreateBox(b);

        // QUESTION: do I just go ahead and do my stock and location update here using another injected repo? 
        // Or should I register another handler for this event that does the update?

        // todo: how to handle read db update errors here? Will need to queue a aggregate replay to rebuild the read db?
    }
}

如果您能为我指明正确的方向,告诉我如何处理读取端更新失败,我会加分。

谢谢!

这取决于什么可以触发框类型更改。如果仅在另一个事件的上下文中更改框类型,则应在该事件处理程序中更新读取模型。如果有专门用于更改框类型的工作流,则应通过新事件和处理程序使用该工作流,以便所有框类型更改都经过相同的逻辑。

我不喜欢在同一个限界上下文中为同一个事件设置多个处理程序,因为这会让人感到困惑。处理程序可能会乱序触发,从而导致难以发现的业务逻辑问题。

在您发布的另一个问题中,您讨论了批量创建框并为每个框生成创建和移动消息。根据您的事件存储,如果创建事件导致框类型更改事件,而移动事件导致不同的框类型更改事件,那么这些事件可能不会按正确的顺序到达。事件的排序取决于事件日志引擎(Kafka 与 Kinesis 不同,与 Event Hub 不同),因此您必须注意它是如何工作的,以确保该框以正确的类型结束。