Jonathan David
Jonathan David

Reputation: 115

CQRS: Higher level state based on multiple aggregates events

consider the following scenario:

we have several aggregates that share a common trait (for example: they belong to the same user) the events of each aggregate are used to materialize its own status.

what if a higher level status is required. that status should represent the overall status of all the user aggregates. consider that there is a special business logic that defines the materialization of the higher level status (for example: if more than x aggregates are in error state, a higher level error status is required on the customer-aggregated state)

this requirement involves materializing over events of multiple aggregates. furthermore, the higher level state is considered as a fact in the sense that it is fully determined by the lower level events. For that reason, it seems that the usage of Saga is inappropriate here.

on the other hand, it is required to issue events when a transition occurs on the higher level state (for example: when more than x aggregates are reporting an error, issue an event that can drive further business logic). so generating events based on other events also seems inappropriate.

What would be the recommended approach here? I can see how a Saga can listen to the lower level aggregate events and transform some of them into commands that go to a higher level entity aggregate, that in turn generates its own events but while taking that route I could not escape the feeling that this event => command => event flow is a ceremony rather than a real design requirement.

thanks for your insights

Jonathan

Upvotes: 2

Views: 1248

Answers (3)

Eben Roux
Eben Roux

Reputation: 13256

When it comes to dealing with processes there are typically two approaches.

Choreography

This relies on an implicit process by having each aggregate take part in a process and the events typically carry all required information. For me this breaks down when part of the process could be split into parallel bits. Having any human-based interaction also does not fit into this too well. There is also no view into the state of the process.

Orchestration

This is where we explicitly track a process and where process managers (I prefer this term above a "saga"). This may be why some folks regard all aggregate roots as process managers but the same aggregate root may partake in different processes which means that if we do regard an AR as a process manager it is going to have to be aware of which process it is part of.

I prefer orchestration since you can view the current state of the process. To this end I tend to make my process managers first-class citizens in that they are also an aggregate root but focus only on process management.

In a bounded context world I will have my process management (higher level) act as a type of integration layer since a process may involve more than one (lower level) bounded context.

For instance, a CustomerInteraction, or a Journal, or EMail may be part of many processes.

On my lower level bounded context level I'd have my message handler respond to commands an I'd publish events. I would not typically react to events on that level.

On the higher level process management bounded context I react to both commands that typically kick off a process and to events that move a process along.

Upvotes: 1

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57377

Udi Dahan once wrote

Here’s the strongest indication I can give you to know that you’re doing CQRS correctly: Your aggregate roots are sagas.

In this case: "it is required to issue events when a transition occurs on the higher level state" is the big clue that there is a "higher level aggregate" that is observing the lower level events and applying your business logic to determine if the higher level state has changed.

This higher level aggregate might have an interface like

void handle(LowerLevelEvent e);

So the lower level aggregates output their events, and then the plumbing carries a copy of that event to the input of the higher level aggregate, and the higher level aggregate outputs the higher level events.

Note that there's no magic here -- the higher level aggregate isn't using all events to decide the higher level transitions, it's only using the ones it currently knows about.

The entire flow looks something like

  • the lower level aggregate broadcasts event:{id:123} to everybody interested
  • the plumbing observes event:{id:123}, and in response dispatches a handle(event:{id:123}) command to the higher level aggregate
  • the higher level aggregate applies its own business logic, broadcasting its own events if appropriate

There no low level and high level.

That's an important point; the low level and high level labels above are really just reference points. As far as an aggregate is concerned, there is just itself, and everything else -- there's no hierarchy of boundaries.

Among other things, this means that you can test the "higher level" aggregate's behaviors without the lower level aggregates even existing - you just send it messages and verify that it reacts correctly.

Upvotes: 3

Constantin Galbenu
Constantin Galbenu

Reputation: 17703

I think that you should focus more on your business than on the implementation design. Is there any business process that needs that information? Then go ahead and implement it.

There no low level and high level. There are just different points of view of the same complex system, different slices that we must do in order to understand it. It's like we can see only 2d but the world is 3d. You have to choose a slice in the reality that is best for you in some circumstance.

In your case you choose to have strong consistency on each Aggregate that use the events to maintain a state use for command validation; they use their own events to have a perspective of the world. But you could have a different view of the world, as real as of that of the aggregates. You could have an Saga/Process manager that listen to the relevant events and based on some business requirements generates some commands that further change the system's state. That is OK, it's how complex systems work. You just have to have clear boundaries around these processes and use bounded contexts and context maps to keep them separated.

In conclusion, go ahead and create Sagas around any business process that you identify.

Upvotes: 0

Related Questions