Sagas
EventFlow provides a simple saga system to coordinate messages between bounded contexts and aggregates.
- Saga identity
- Saga
- Saga locator
- Zero or more aggregates
This example is based on the chapter "A Saga on Sagas" from the CQRS Journey by Microsoft, in which we want to model the process of placing an order.
- User sends command
PlaceOrder
to theOrderAggregate
OrderAggregate
emits anOrderCreated
eventOrderSaga
handlesOrderCreated
by sending aMakeReservation
command to theReservationAggregate
ReservationAggregate
emits aSeatsReserved
eventOrderSaga
handlesSeatsReserved
by sending aMakePayment
command to thePaymentAggregate
PaymentAggregate
emits aPaymentAccepted
eventOrderSaga
handlesPaymentAccepted
by emitting aOrderConfirmed
event with all the details, which via subscribers updates the user, theOrderAggregate
and theReservationAggregate
Next, we need an ISagaLocator
which basically maps domain events to a
saga identity allowing EventFlow to find it in its store.
In our case, we will add the order ID to the event metadata of all events related to a specific order.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Alternatively, the order identity could be added to every domain event
emitted from the OrderAggregate
, ReservationAggregate
, and
PaymentAggregate
aggregates that the OrderSaga
subscribes to,
but this would depend on whether or not the order identity is part of
the ubiquitous language for your domain.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Attention
Even though the method for publishing commands is named
Publish
, the commands are only published to the command bus
after the aggregate has been successfully committed to the event
store (just like events). If an unexpected exception is thrown by this
command publish, it should be handled by a custom implementation of
ISagaErrorHandler
.
The next few events and commands are omitted in this example, but at last the
PaymentAggregate
emits its PaymentAccepted
event and the saga
completes and emits the final OrderConfirmed
event.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Note
An AggregateSaga<,,>
is only considered in its running
state if there has been an event and it hasn't been marked as completed
(by invoking the protected
Complete()
method on the
AggregateSaga<,,>
).
Alternative saga store
By default, EventFlow is configured to use event sourcing and aggregate
roots for the storage of sagas. However, you can implement your own storage
system by implementing ISagaStore
and registering it.