Есть ли у вас опыт работы с паттерном Saga?

«Есть ли у вас опыт работы с паттерном Saga?» — вопрос из категории Паттерны, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, применял паттерн Saga для управления распределенными транзакциями в микросервисных архитектурах, где классические ACID-транзакции через одну БД невозможны.

Суть паттерна: Длинная бизнес-транзакция разбивается на последовательность локальных транзакций в отдельных сервисах. Каждая локальная транзакция публикует событие, которое запускает следующий шаг. В случае сбоя любого шага выполняются компенсирующие транзакции (Compensating Transactions) для отката уже выполненных действий.

Два основных стиля реализации:

  1. Оркестрация (Orchestration): Центральный координатор (оркестратор) управляет потоком выполнения и откатом.

    • Плюсы: Централизованный контроль, проще отслеживать состояние.
    • Минусы: Точка централизации, оркестратор может стать "тяжелым".
      // Упрощенный пример оркестратора на C#
      public class OrderCreationSagaOrchestrator
      {
      public async Task Execute(OrderData order)
      {
          try
          {
              await _inventoryService.ReserveItemsAsync(order.Items); // Шаг 1
              await _paymentService.ChargeAsync(order.PaymentDetails); // Шаг 2
              await _orderService.CreateOrderAsync(order); // Шаг 3
              // Все шаги успешны -> Saga завершена
          }
          catch (Exception ex)
          {
              // Компенсация в обратном порядке
              await _orderService.CancelOrderAsync(order.Id);
              await _paymentService.RefundAsync(order.PaymentDetails);
              await _inventoryService.ReleaseItemsAsync(order.Items);
              throw new SagaFailedException("Order creation failed", ex);
          }
      }
      }
  2. Хореография (Choreography): Каждый сервис слушает события от предыдущего и запускает свой шаг, публикуя событие для следующего.

    • Плюсы: Полная децентрализация, слабая связанность.
    • Минусы: Сложнее отслеживать общее состояние, может возникнуть циклическая зависимость событий.

Критически важные практики:

  • Идемпотентность: Все шаги и компенсации должны быть идемпотентными (повторный вызов с теми же данными не должен менять результат), чтобы безопасно обрабатывать повторные сообщения.
  • Надежная доставка сообщений: Использование брокеров сообщений (Kafka, RabbitMQ) с подтверждениями (acknowledgments).
  • Хранение состояния: Состояние саги (например, SagaState: Pending, Executing, Compensating, Completed, Failed) должно сохраняться в персистентном хранилище.

Имею практический опыт реализации саг с использованием фреймворков (MassTransit, NServiceBus) и создания кастомных решений на основе фоновых заданий (Hangfire/BackgroundService) и таблиц состояний в БД.