DEV Community

Tharindu Lakshan
Tharindu Lakshan

Posted on

Saga Orchestration in .NET with CQRS, Event Sourcing, Hydration & Event Propagation

🧩 Saga Orchestration in .NET with CQRS, Event Sourcing, Hydration & Event Propagation

A beginner-friendly explanation

Modern distributed systems often need to coordinate long-running workflows such as order processing, payment authorization, inventory reservation, and shipping. These workflows involve multiple microservices communicating asynchronously. To manage this safely, we use Sagas.

This article explains how Sagas work in .NET and how they combine naturally with CQRS, Event Sourcing, and event propagation using tools like RabbitMQ and MassTransit.


⭐ 1. What is a Saga?

A Saga is a pattern for managing long-running, multi-step business processes that span multiple services.

A Saga helps you coordinate a workflow like:

  1. Create Order
  2. Reserve Inventory
  3. Process Payment
  4. Arrange Shipment

And it also defines compensation steps when something fails:

  • If payment fails → release inventory
  • If shipping fails → refund payment

A Saga ensures the full workflow completes successfully, or the system gracefully rolls back using compensating actions.


⭐ 2. How CQRS fits into a Saga

CQRS splits the system into two sides:

Write Side (Commands)

Each step of a Saga is triggered by a command:

  • ReserveInventoryCommand
  • ProcessPaymentCommand
  • ShipOrderCommand

Commands change state inside aggregates and generate events.

Read Side (Queries)

The read side builds projections of:

  • Saga state (current step, status)
  • Order status
  • Payment status

This helps each service know what stage the workflow is in.

CQRS gives clarity and removes the need for shared databases between services.


⭐ 3. How Event Sourcing supports a Saga

With Event Sourcing, every state change is saved as an event:

  • OrderCreated
  • InventoryReserved
  • PaymentAuthorized
  • ShipmentScheduled

Each event becomes a reliable message that the Saga listens to.

The Saga then decides the next action based on the event.

For example:

OrderCreated → Start Saga → Send ReserveInventoryCommand
InventoryReserved → Send ProcessPaymentCommand
PaymentAuthorized → Send ShipOrderCommand
Enter fullscreen mode Exit fullscreen mode

If anything fails:

PaymentFailed → Send ReleaseInventoryCommand → Mark Saga as Failed
Enter fullscreen mode Exit fullscreen mode

⭐ 4. Hydration: rebuilding Saga state when needed

Because events are stored chronologically, the Saga can rebuild its state—this is called hydration.

When a Saga instance loads:

var events = eventStore.LoadStream(sagaId);

foreach (var e in events)
{
    saga.Apply(e);
}
Enter fullscreen mode Exit fullscreen mode

Hydration ensures:

  • You don’t need to store complex Saga state in SQL
  • The full workflow history is always available
  • You can rebuild state anytime

⭐ 5. Event Propagation: how services communicate

After the write side stores events, they must be propagated so other services can react.

There are two types of propagation:

🔹 Internal propagation (inside same service)

  • Use MediatR Notifications
  • Update projections
  • Trigger in-process handlers

🔹 External propagation (between microservices)

Use messaging systems such as:

  • MassTransit (preferred in .NET; high-level abstraction)
  • RabbitMQ
  • Kafka
  • Azure Service Bus

Flow:

  1. Aggregate emits events
  2. Event store saves them
  3. Event Publisher sends them to exchanges/topics
  4. Other services/processes receive and react

Example with RabbitMQ:

await bus.Publish(new InventoryReserved(orderId));
Enter fullscreen mode Exit fullscreen mode

Example with MassTransit:

await _publishEndpoint.Publish(new PaymentAuthorized(orderId));
Enter fullscreen mode Exit fullscreen mode

These messages trigger the next command in the Saga.


⭐ 6. Saga Orchestration Flow (Simple Example)

Let’s use a basic e-commerce workflow.

Step 1 — Order Created

  • Saga starts
  • Saga publishes ReserveInventoryCommand

Step 2 — Inventory Reserved

  • Saga publishes ProcessPaymentCommand

Step 3 — Payment Authorized

  • Saga publishes ShipOrderCommand

If Something Fails

  • Saga sends compensating commands:

    • ReleaseInventoryCommand
    • CancelOrderCommand
    • RefundPaymentCommand

This ensures the system is always consistent.


⭐ 7. Saga State Machine (MassTransit Example)

MassTransit offers built-in Saga state machine features.

public class OrderState : SagaStateMachineInstance
{
    public Guid CorrelationId { get; set; }
    public string CurrentState { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Saga state machine:

public class OrderSaga : MassTransitStateMachine<OrderState>
{
    public State InventoryReserved { get; private set; }
    public State PaymentCompleted { get; private set; }

    public Event<OrderCreated> OrderCreated { get; private set; }

    public OrderSaga()
    {
        InstanceState(x => x.CurrentState);

        Initially(
            When(OrderCreated)
                .Then(context => { /* start saga */ })
                .TransitionTo(InventoryReserved)
                .Publish(context => new ReserveInventoryCommand(context.Message.OrderId))
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

MassTransit handles:

  • Saga persistence
  • Message routing
  • Correlation IDs
  • Retries
  • Error handling

⭐ 8. Why use Saga + CQRS + ES together?

Feature Benefit
Event Sourcing Full history, hydration, audit trail
CQRS Clean separation of writes/reads
Saga Manage long workflows with compensation
RabbitMQ/MassTransit Reliable async communication
Event Propagation Each service reacts without tight coupling

They form a strong pattern for distributed systems.


⭐ Final Summary

Saga orchestration becomes powerful when combined with CQRS and Event Sourcing:

  • CQRS handles clear separation of commands and queries
  • Event Sourcing makes state changes traceable and rebuildable
  • Hydration recreates the saga state from events
  • Event Propagation uses RabbitMQ or MassTransit to connect microservices

* Sagas coordinate multi-step workflows and ensure consistency through compensation


Sample GitHub Project

https://github.com/CoorayNTL/MiniCommercePlatform

Top comments (0)