apache-kafkadomain-driven-designcqrsevent-sourcingeventstoredb

Is Combining Event Store and Event Bus Recommended in DDD with Event Sourcing?


I'm currently exploring Domain-Driven Design (DDD), Event Sourcing, and CQRS. I'm seeking clarity on whether it's advisable to use a single component for both the Event Store and the Event Bus. There's a variety of perspectives in the literature, with many advocating for a unified component. My concerns revolve around scalability, efficiency, and the implications for system architecture.

I would also appreciate recommendations on the best technologies suited for implementing this setup. Furthermore, if we opt for a combined Event Store and Event Bus, how do we effectively differentiate between domain events and system events? This distinction seems crucial for maintaining clarity and avoiding potential conflicts in event processing.

Could you please share your experiences or insights on these aspects? In a microservices environment where event sourcing is applied selectively, I'm curious about how a combined solution for the Event Store might affect system decoupling and flexibility

I have tried using EventStoreDB for both event store and event bus, but i am now not able to different between Domain Events and Local Service Events


Solution

  • There's no particular "preferred" way to do the split between data on the inside and on the outside (that's what you're looking at).

    If your event store supports messaging capabilities, you can use them. If you aim to strongly separate data between bounded contexts, you'd prefer for each bounded context to keep their data in a separate store, and integrate via some message broker. If you have one team maintaining software for several bounded contexts, you can keep things in one store and agree on context-specific separation convention (like stream name prefixes, etc) to allow logical separation of data. Keeping events in separate stores is beneficial in case of different operational requirements for different contexts, like one module takes lots of load, and it could affect the performance of the other module if they integrate via the share store.

    One thing to note here is that integration of components within a single bounded context doesn't require using data on the outside as it becomes meaningless. Even though you can split software for one bounded context in different components, the logical context boundary remains the same, and there's a single domain model. Therefore, when you implement processes that involve aggregates from the same context, you don't need to use "public events", nor you need an external broker component for integration (again, if your event store database supports messaging).

    EventStoreDB does provide messaging capabilities. In particular, persistent subscriptions allow you to implement reactive processes and process managers using consumer groups. Lack of guaranteed ordered delivery doesn't hurt in integration scenarios anyway.

    If you do want to use ESDB for messaging across bounded contexts, you can, again, use persistent subscriptions for transformations, or use custom projections for the same purpose. Both of those features have their weaknesses, where the most obvious one is write amplification, but you expect to produce new events anyway, so it might be less of a concern. The largest issue with those features as per now is that they run exclusively on the leader node, which also supports the core transactional read and write load.

    You can use ready-made third-party tools like Eventuous Gateway (if you're using .NET) or implement your own. Basically, when the inside-outside split is required, the implementation always looks like Consume -> Transform -> Produce. When choosing the place where those public events will be produced to, consider what other people in your organisation are using for integration.

    One last thing to remember there is the reason for splitting inside and outside data. Yes, sometimes events on the outside need to be richer than internal domain events. Sometimes, it's the opposite, and you want to hide something. But most importantly you want to split due to the data format. Public events schema is a contract, while private events schema is, essentially, a part of your domain model. One definitely wouldn't want to couple their domain model to the public contract.