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



我已经实现了一个事件存储系统,其中我的根聚合是按照仓库建模的。例如,我有一些活动,例如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?
}
}

如果您能为我指出如何处理读取端更新失败的正确方向,那就太好了。

谢谢!

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

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

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

最新更新