Search code examples
c#cqrsevent-sourcingeventual-consistencymediatr

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


I have implemented an event store system where my root aggregate is modeled after a warehouse. I have some events like BoxCreated and BoxLocationChanged for example. Both of these events have separate event handlers that update a read side table dbo.Boxes.

However, I have now come to the point where both of these handler's will each need to update another read side DB dbo.BoxType which tracks the state of stock and location per box type.

My question is if I should create a separate event handler (using the same events) for these new read side updates, or should I just inject another repository into the current event handlers and process all the read side updates in the same event handlers?

Which is the preferred design choice? Is the choice dependent on other factors I am not listing?

To demonstrate, using MediatR, I have currently have event handlers that look like:

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?
    }
}

Bonus if you can point me in the right direction on how to handle read-side update failures.

Thanks!


Solution

  • This depends on what can trigger a box type change. If box type changes are only made in the context of another event, then the read model should be updated inside this event handler. If there is a workflow specifically for changing a box type, that should be used via a new event and handler so that all box type changes go through the same logic.

    I'm not a fan of having multiple handlers for the same event in the same bounded context because it can get confusing. The handlers can fire out of order resulting in business logic problems that are difficult to find.

    In the other question you posted you discussed creating boxes in batches and generating create and move messages for each. Depending on your event store, if a create event results in a box type change event, and a move event results in a different box type change event, those events may not arrive in the proper order. Ordering of events is event log engine dependent (Kafka is different than Kinesis is different than Event Hub) so you would have to pay attention to how that works to ensure the box ends up with the proper type.