Previous Service-Discovery-and-health-Check Eventual Consistency Next

Saga Pattern in .NET Core

⚑ Saga Pattern in .NET Core

πŸ“– What is Saga Pattern?

The Saga Pattern is used in microservices to manage distributed transactions. Instead of a single ACID transaction across services, it breaks the workflow into smaller steps (local transactions). If one step fails, compensating transactions roll back the previous steps.

πŸ›  Example in .NET Core

Imagine an Order Service that coordinates with Payment Service and Inventory Service.

    // Order Saga Orchestrator (simplified)
    public class OrderSaga
    {
        public async Task ExecuteAsync()
        {
            try
            {
                await ReserveInventory();
                await ProcessPayment();
                await ConfirmOrder();
            }
            catch (Exception)
            {
                await Compensate();
            }
        }

        private Task ReserveInventory() => Task.Run(() => Console.WriteLine("Inventory Reserved"));
        private Task ProcessPayment() => Task.Run(() => Console.WriteLine("Payment Processed"));
        private Task ConfirmOrder() => Task.Run(() => Console.WriteLine("Order Confirmed"));

        // Compensation logic
        private Task Compensate() => Task.Run(() => Console.WriteLine("Rollback: Cancel Payment & Release Inventory"));
    }
    

βœ… Advantages

  • Ensures data consistency across microservices without 2PC (two-phase commit).
  • Improves fault tolerance with compensating transactions.
  • Scales well in distributed systems.
  • Supports long-running business processes.

⚠️ Disadvantages

  • Complex to design and implement.
  • Compensating logic can be tricky and error-prone.
  • Eventual consistency (not immediate consistency).
  • Harder debugging due to asynchronous flows.

🧭 Best Practices

  • Use an Orchestration-based Saga (central coordinator) for complex workflows.
  • Use Choreography-based Saga (event-driven) for simpler flows.
  • Clearly define compensating transactions for each step.
  • Implement idempotency to avoid duplicate operations.
  • Use message brokers (e.g., RabbitMQ, Kafka, Azure Service Bus) for reliability.

πŸ”’ Precautions

  • Ensure compensating transactions do not cause further inconsistencies.
  • Monitor and log each saga step for observability.
  • Handle retries and timeouts gracefully.
  • Secure inter-service communication.

🎯 Summary

The Saga Pattern is a powerful way to handle distributed transactions in microservices. In .NET Core, it can be implemented using orchestration or event-driven choreography. While it adds complexity, it ensures resilience and consistency in large-scale systems.

πŸš€ Saga Pattern with MassTransit in .NET Core

πŸ“– What is MassTransit Saga?

MassTransit is a popular .NET library for message-based distributed applications. It provides built-in support for the Saga Pattern, which helps manage long-running workflows across multiple services using state machines and message queues.

πŸ›  Example: Order Processing Saga

Consider an e-commerce flow: Order Service β†’ Payment Service β†’ Shipping Service. If payment fails, the saga compensates by canceling the order.

    using MassTransit;
    using System;

    // Define Saga State
    public class OrderState : SagaStateMachineInstance
    {
        public Guid CorrelationId { get; set; }
        public string CurrentState { get; set; }
        public string OrderId { get; set; }
    }

    // Define Events
    public class OrderSubmitted
    {
        public string OrderId { get; set; }
    }

    public class PaymentCompleted
    {
        public string OrderId { get; set; }
    }

    public class PaymentFailed
    {
        public string OrderId { get; set; }
    }

    // Define State Machine
    public class OrderStateMachine : MassTransitStateMachine<OrderState>
    {
        public State Submitted { get; private set; }
        public State Completed { get; private set; }
        public State Failed { get; private set; }

        public Event<OrderSubmitted> OrderSubmittedEvent { get; private set; }
        public Event<PaymentCompleted> PaymentCompletedEvent { get; private set; }
        public Event<PaymentFailed> PaymentFailedEvent { get; private set; }

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

            Event(() => OrderSubmittedEvent, x => x.CorrelateById(m => m.Message.OrderId));
            Event(() => PaymentCompletedEvent, x => x.CorrelateById(m => m.Message.OrderId));
            Event(() => PaymentFailedEvent, x => x.CorrelateById(m => m.Message.OrderId));

            Initially(
                When(OrderSubmittedEvent)
                    .Then(context => context.Instance.OrderId = context.Data.OrderId)
                    .TransitionTo(Submitted));

            During(Submitted,
                When(PaymentCompletedEvent)
                    .TransitionTo(Completed),
                When(PaymentFailedEvent)
                    .TransitionTo(Failed)
                    .Then(context => Console.WriteLine("Compensating: Cancel Order")));
        }
    }
    

βœ… Advantages

  • Handles long-running workflows across services.
  • Built-in state persistence and correlation.
  • Supports retries, compensation, and fault tolerance.
  • Integrates with message brokers (RabbitMQ, Azure Service Bus, Kafka).

⚠️ Disadvantages

  • Increases system complexity.
  • Requires reliable message broker setup.
  • Eventual consistency (not immediate consistency).
  • Debugging distributed sagas can be challenging.

🧭 Best Practices

  • Use idempotent consumers to avoid duplicate processing.
  • Persist saga state in a reliable store (SQL, MongoDB, etc.).
  • Define clear compensating actions for each step.
  • Monitor saga execution with logging and tracing.

πŸ”’ Precautions

  • Ensure correlation IDs are unique and consistent.
  • Handle message retries and poison messages gracefully.
  • Secure communication between services and broker.
  • Test compensation logic thoroughly.

🎯 Summary

MassTransit makes implementing the Saga Pattern in .NET Core much easier by providing state machines, persistence, and message-driven orchestration. It’s ideal for distributed systems where workflows span multiple services and need resilience.

Back to Index
Previous Service-Discovery-and-health-Check Eventual Consistency Next
*