EventSourcing inside and outside Bounded Contexts

725 Views Asked by At

I'm trying to implement an event sourced system with dddd. Currently I'm struggling how and where my events are crossing the boundaries of the bounded contexts.

Imagine there are two bounded contexts:

  1. Product Management
  2. Logistics System

Product Management has all the knowledge about the products. For simplification it is just "Name". The logistics system also has products, but has no knowledge about their meta data. For them it is mostly only a physical box with an Id. But when somebody scans this product, they want to show the name either. So the ProductManagement BC should inform the Logistics BC, that a product is registered and a name has changed. So I will end up with the events in ProductManagement, raised from inside the ProductAggregate:

ProductManagement.Events.ProductRegistered
ProductManagement.Events.ProductNameChanged

When I got it correctly these are the events which I will save into the event store. And these are also the events which will be published into the message bus. So at the logistics side I will subscribe to these events. So far so good.

The problem now is: How will I work with this event on the Logistics side? Vaughn Vernon said in a talk, that it is best practice to have an event handler there, which is in the application layer, so it will basically be an application service. He also said, that it would be best to transform it to one or several commands. Do I save all received events on the logistics side again? Do I also save the commands? How can I reproduce my current state if something went wrong? Or how will I know, that it is not the fault of the processing in the receiving Bounded Context, but rather a wrong event. What will I do if my transformed commands getting rejected?

I know that there are no calculations or changes in aggregates on logistics side. But I think this doesn't really matter for my questions.

3

There are 3 best solutions below

0
On

[...] He also said, that it would be best to transform it to one or several commands. Do I save all received events on the logistics side again? Do I also save the commands?

First, you have to ask the domain experts if that event will cause a side effect that impact the LS context. Only in this case, you have to subscribe to this event and send the related command to the LS Aggregate that will change and commit its state or, if you choose to event-source this aggregate too, another event.

How can I reproduce my current state if something went wrong? Or how will I know, that it is not the fault of the processing in the receiving Bounded Context, but rather a wrong event? What will I do if my transformed commands getting rejected?

An event is a representation of something happened, so it can't be "wrong". Anyway, commands triggered by an event can fail. Which type of failure are you talking about? Technical or domain specific? In the first case, the source event will stay in the bus for a future retry (maybe after some bug fix). In the second case, if the PM aggregate needs to be informed about the result, the LS Aggregate should emit an appropriate event which, in turn, will be handled by the PM aggregate.

0
On

Couple of things here.

First, you do not have to import the Logistics BC about name changes. You can get this information from the PM BC when needed, from the client. This is usually done by some sort of composite UI. The UI composition can be done on the client or on the (web) server. You may want to check the article The secret of better UI composition by Mauro Servienti, describing this.

But in general, this usually works like this:

domain event -> pub/sub -> message consumer -> command -> domain command handler

So,

  1. you publish your domain event to the bus, from the PM BC
  2. there is an event handler for this event in the Logistics
  3. the event handler may do some checks, and send the RegisterProduct command to the same BC
  4. the command is handled as usual and new Product aggregate is created in the Logistics

It works like this not only in event-sourced system but in any system with multiple services, using event-driven architecture.

0
On

For the use case you describe, you just need some properties of the product to be used by the logistics system. The logistics system therefor could keep a local cache of the product information it needs by subscribing to the events you describe - this could be a simple in-memory cache. They don't need converting into commands or anything like that as you're dealing with the read model I.e. A view. Just have a simple event handler handle the event and update some state somewhere - no need to event source it on the read side. When the logistics system needs the name of the product, it just gets it from its local cache. You haven't broken the autonomy of the two contexts as Product Management is still the source of truth.

If you ever need to rebuild the state you can just wipe the cache and replay all events through your handlers. But remember the Product Management context owns these events so they should only be saved there, not in the Logistics context - you would need a way of republishing them if you ever wanted to rebuild state

By the way this series of blog posts describes this exact use case:

https://www.tigerteam.dk/2014/micro-services-its-not-only-the-size-that-matters-its-also-how-you-use-them-part-1/

(At part 5 if I recall correctly)

Alternatively you can do some sort of UI composition where the name is taken from the Product Management context and the other details from the Logistics context (also discussed in the above blog posts)