首页 > 解决方案 > 读取端数据库最终与事件处理程序保持一致。如何分段读取端更新

问题描述

我已经实现了一个事件存储系统,其中我的根聚合以仓库为模型。我有一些事件BoxCreatedBoxLocationChanged例如。这两个事件都有单独的事件处理程序来更新读取侧表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?
    }
}

如果您可以为我指出如何处理读取端更新失败的正确方向,则可以加分。

谢谢!

标签: c#cqrsevent-sourcingeventual-consistencymediatr

解决方案


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

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

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


推荐阅读