3

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?

4

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!

  • 1
    After 5 years of building distributed occasionally disconnected systems based on event sourcing, and many years of CRUD development prior to that, I can tell you that event sourcing introduces a lot of low-level complexities (read my blog posts here). However, this is just a different kind of complexity than what you face when you build a domain that needs to map to an RDBMS. It's not more complexity. – Dennis Doomen Oct 16 '18 at 6:08
  • @DennisDoomen I agree ES introduces a different kind of complexity, but that complexity is additive. That is, in addition to the complexities of managing/persisting a domain model, ES adds complexity to persistence/management. Imagine a system with a single entity. Certainly it is less complex to read/write to this entity using a CRUD approach than ES. As my post explains, the addition of all the complexities of ES (schema, versioning, etc) is justified when the benefits (history, auditing, scaling) are necessary. This is precisely why ES is not a good fit for a top-level architecture. – king-side-slide Oct 16 '18 at 15:39
  • 1
    Although I agree with most of what you're saying, I've seen many teams fail in trying to effectively map a domain model to a RDBMS using an OR/M. Loads of performance problems and other impedance mismatches. – Dennis Doomen Oct 18 '18 at 10:27
  • 1
    @DennisDoomen If an ORM is not the right tool for the job, it should not be used. I don't find an ORM particularly useful in a DDD system anyway due to the additional mapping required from the data model an ORM necessitates to the domain model and visa versa. Impedance mismatch is orthogonal to the use of an ORM and can exist in any system backed by an RDBMS (graph vs tabular). In any case, modeling is hard. I've seen teams fail in all sorts of ways. The reason is always the same: a lack of understanding. – king-side-slide Oct 18 '18 at 14:30
  • @king-side-slide wait a minute. so you are saying that it's the event sourcing that can't be a top level architecture but CQRS can be? I also watched that talk (Greg Young) on youtube very recently and he says CQRS is not a fit for top level. Im confused. – Simple Fellow Dec 4 '18 at 2:30
0

Your approach is flawed.

Consider a business rule for the 'sales context'

If a customer changes their email, they are credited £10

The 'sales context' Customer applies this credit when the Email is set and reconstitutes itself correctly from the EmailChanged event

The 'account context' Customer however is ignoring EmailChanged events. When it was programmed the £10 rule had not been invented. So it does not change and the customer's balance is incorrect when viewed by the accounts application.

By populating two distinct objects from the events you are doing exactly that. You don't have two 'views' of the same Customer you have two alternate universe Customers. Each looking as it would if only a subset of your business rules existed.

Edit - summary of comment discussion

It seems your view is that Events should only ever do a single thing when applied to an Object, updating a property to the value stored in the event.

If something does more than a single thing it is a Command, and may generate multiple Events. ie EmailChanged, AccountIncremented

My view is that this misses a huge chunk of Event Sourcing, namely that the function applying an event to a model can be anything you like. When an EmailChanged event is applied to a Customer, leaving aside the domain boundary challenging account example, you might:

  • trim spaces from the end of the address
  • split the address into username and domain
  • add the old address to a list of old email addresses
  • set or reset an InvaildEmail flag

Note that these are all internal changes to the Customer object. They don't generate new events and if they did it would be problematic.

The flaw in your ignoring events approach is that you assume that an event is bounded to a domain and could never have a cross domain effect.

Yes good design suggests that if changing an email address credits the customer account, then it should trigger an External AccountCredit event which is passed to the account domain as in your command example.

But then I chose an obvious example. Who is to say that there isn't a more subtle error, maybe there is an extra fraud check when the InvalidEmail flag is set for example

  • Could not that business rule in the Sales context either generate an AccountCredited(10) event or somehow issue the relevant command to the Account context? The Account context would only care that the account was credited, not what caused it. – Entith Jul 9 '18 at 23:12
  • no, because then you would confuse the event source events and events. when you reconstitute the customer you would get duplication – Ewan Jul 9 '18 at 23:29
  • I'm sorry, I don't follow. The only "events" in this situation are event source events. – Entith Jul 9 '18 at 23:35
  • in which case you can't have your objects create more in response to one. You need to understand the difference between general events in your solution and the event stream you use to persist the object – Ewan Jul 10 '18 at 6:30
  • The AccountCredited(10) event and the EmailChanged event would both be generated as a result of some ChangeCustomerEmail command issued to the Sales context, not one as a reaction to the other. I'm not sure what you mean by "general event". The only events I have talked about are the event sourcing events used to persist the object. – Entith Jul 10 '18 at 7:57
0

Yes, you will have some pain at first, but not a fraction of the cumulative pain you will endure from maintaining an UN-SOLID monolith as it gets more and more complex.

CQRS and Event Sourcing is the most natural implementation of non-trivial business models. Think of EVERYTHING that happens around you every day. Nothing starts without a trigger (Command) and everything leads to a series of Events. The notion that a domain model consists of only Classes and Relationships is a relic from the 90’s when database persistence was in it’s infancy.

If you compose your models of Entities, Relationships, Aggregate Roots, Value Object’s, Commands and Events, the following starts to happen:

  1. You get greater Single Responsibility because the workload gets spread to where it should be. (Big bloated controller’s in MVC anyone :-)

  2. Almost half of your bugs are caused by objects not being in their expected state. Since Event Sourcing breaks up your mammoth classes and leads you to immutability, you will have fewer bugs.

  3. You get semi-decent workflow for free.

  4. You can ask complicated questions of your data (which item in my store gets added and then removed the most?)
  5. You get Auditing for free.
  6. You get great performance by preparing your view models with Projections.
  7. All-round maintainability is generally better.
  8. Testing is easier (setting up integration testing data is much easier)

An ES system is going to be more difficult to conceptualize

On the contrary, It's way more intuitive for complex models.

Not only will you likely lose many of the built-in benefits

You want to get rid of them, they are holding you back on the Command side. They are still your friend on the Query side.

because your domain is simply a projection of the event store

Your event store IS your domain model, spread over time. Projections are view models, de-normalized data sets that you present to clients.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.