CQRS Event Sourcing – Anti-Pattern as Top Level Architecture

anti-patternsArchitecturecqrsdomain-driven-designevent-sourcing

I have been study DDD along with CQRS and Event Sourcing. I recently listened to a talk Greg Young gave a couple years ago where he said that CQRS and Event Sourcing are not a top level architecture and should be used within the confines of specific bounded contexts. Doing otherwise would be an anti-pattern.

This makes a lot of sense in general, however it seems to conflict with what I perceived to be one of the benefits of an event sourced system: being able to have different context-specific aggregate roots of the same "thing" in different contexts, each reconstructed from the same event stream.

For example a Customer would have different implementation in different contexts (Ordering, Marketing/Product Recommendations, Customer Service, Authentication), however certain events would be relevant across multiple contexts (eg. CustomerEmailChanged would be relevant to Marketing, Customer Service, and Authentication. OrderPlaced would be relevant to Ordering and Marketing).

Prior to listening to that talk, my plan was to have these different contexts share an event store and just ignore events that were not relevant. And that stills seems like a very reasonable way to handle that, but I also can kind of see how this could be an anti-pattern, blurring the lines between various bounded contexts.

What would be the correct way to go about this, or is my approach altogether flawed?

Best Answer

The reason ES is generally not a good idea for top-level architecture ("anti-pattern" has no real meaning anymore) is because it's complicated. Plain and simple.

An ES system is going to be more difficult to conceptualize and develop than a traditional system backed by an RDBMS. Think about it. You are essentially adding another, lower layer (a new "truth") to your application along with all of the plumbing necessary to "project" that layer upward towards your domain. This is not trivial. Not only will you likely lose many of the built-in benefits an RDBMS can provide regarding constraints, type checking, and normalization of your data (an event store is usually a little more free-form), you need to account for things like possible future changes (versioning) and the headache of "retroactively" modifying your event store when you realize you need to fundamentally change some behavior (I promise you this will happen for any non-trivial system). Taken as a whole, ES presents a system that is harder to understand, harder to manage, and harder to change (this may seem like a point of contention, but a well-designed traditional DDD system is easier to change. That's the whole point of DDD right?).

On the other side of the coin, most of the benefits of ES are high-level in nature (introducing another layer to your application certainly doesn't benefit developers). One of the biggest benefits is that ES systems are easier to scale. A single, append-only store with separate read/write models can be scaled with ease. Additionally, as @Ewan alluded to, because your domain is simply a projection of the event store (that is, because you have added another layer), ES systems afford the ability to easily create many different projections of your data (a new command model can emerge without having to make DB schema changes, for example). This can be useful for data warehousing/analytics, but also can help with future-proofing your application.

Now, with the above in mind, we can clearly see that the benefits of an ES system are orthogonal to it's drawbacks: high-level benefits, low-level drawbacks. This is in direct contrast to most other architectures: DDD is all about creating a system that is simple to develop and change at the cost of possible difficulties in scaling etc. This is generally a better trade-off for software systems because they tend to evolve over time, hardware is cheap, and most applications simply don't require a massive scale/throughput.

As such, ES should applied when necessary to help solve a specific problem/domain, not blindly to an entire application (pet projects exempt).

EDIT - As to the question regarding "sharing" events between contexts. Each context needs to "own" it's changes. You don't want a change in one context to directly create a change in another. Doing so will couple them together which defeats the entire purpose of creating two contexts in the first place. This is akin to having two aggregates depend on the same field in a RDBMS. I think you may be misunderstanding the benefit you outline regarding the reconstruction of different models from your event stream. This DOES NOT APPLY to command models. You may only have ONE command model per event stream. Business rules cannot be selectively enforced. And to that end, multiple read models are possible in any system.

More specifically, ask yourself "why" your EmailChanged event is relevant to more than one context. Could it be possible that you haven't divided your contexts appropriately? It looks like you have created contexts around organizational boundaries. This often creates problems. Here is a short discussion of the topic:

http://udidahan.com/2016/02/19/ask-udi-two-services-operating-on-the-same-entity/

It's important to understand that your entities and bounded contexts will often be "discovered", not decided upon based on some holistic knowledge of your business. Model according to behavior!

Related Topic