← Назад к вопросам
Что такое паттерн SAGA?
2.4 Senior🔥 131 комментариев
#REST API и микросервисы#SOLID и паттерны проектирования
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
SAGA паттерн: управление распределенными транзакциями
SAGA — это паттерн для управления долгоживущими транзакциями в микросервисной архитектуре, когда нет единой ACID базы данных.
Проблема: ACID транзакции в микросервисах
Одна БД (монолит) → одна транзакция ✓
Multiple DBs (микросервисы) → как откатить все, если что-то упадет?
Пример:
Пользователь заказывает билет:
1. Reserve ticket in TicketService
2. Charge payment in PaymentService
3. Send confirmation email in NotificationService
Что если:
- Билет зарезервирован ✓
- Платеж упал ❌
- Что делать с резервой?
Традиционная ACID транзакция здесь невозможна, потому что это разные сервисы и БД.
SAGA решение
SAGA — это последовательность локальных транзакций, где каждая база может откатить свои изменения (компенсирующая транзакция).
Транзакция 1: Reserve ticket ✓ Откат: Cancel reservation
Транзакция 2: Charge payment ✓ Откат: Refund money
Транзакция 3: Send email ✓ Откат: Send cancellation email
Два типа SAGA
1. Choreography (Танец без дирижера)
Каждый сервис слушает события и решает, что делать:
OrderService: "Заказ создан" → Публикует OrderCreatedEvent
↓
TicketService слушает OrderCreatedEvent
→ Резервирует билет
→ Публикует TicketReservedEvent
↓
PaymentService слушает TicketReservedEvent
→ Берет платеж
→ Публикует PaymentChargedEvent
↓
NotificationService слушает PaymentChargedEvent
→ Отправляет email
Если PaymentService падает:
PaymentService: "Платеж упал" → Публикует PaymentFailedEvent
↓
TicketService слушает PaymentFailedEvent
→ Отменяет резервацию (компенсирующая транзакция)
→ Публикует TicketReleasedEvent
На Java с Spring Cloud Stream:
// OrderService
@Service
public class OrderService {
private final OrderRepository orderRepo;
private final StreamBridge streamBridge;
public void createOrder(Order order) {
orderRepo.save(order);
// Публикуем событие
streamBridge.send(
"order-events",
new OrderCreatedEvent(order.getId(), order.getCustomerId())
);
}
}
// TicketService
@Service
public class TicketService {
private final TicketRepository ticketRepo;
private final StreamBridge streamBridge;
@Bean
public Consumer<OrderCreatedEvent> reserveTicket() {
return event -> {
try {
// 1. Локальная транзакция
TicketReservation reservation = new TicketReservation();
reservation.setOrderId(event.getOrderId());
reservation.setStatus("RESERVED");
ticketRepo.save(reservation);
// 2. Публикуем успех
streamBridge.send(
"ticket-events",
new TicketReservedEvent(event.getOrderId())
);
} catch (Exception e) {
// 3. Публикуем ошибку → другие сервисы откатятся
streamBridge.send(
"ticket-events",
new TicketReservationFailedEvent(event.getOrderId())
);
}
};
}
@Bean
public Consumer<PaymentFailedEvent> releaseTicket() {
return event -> {
// Компенсирующая транзакция
TicketReservation reservation = ticketRepo.findByOrderId(event.getOrderId());
reservation.setStatus("RELEASED");
ticketRepo.save(reservation);
};
}
}
// PaymentService
@Service
public class PaymentService {
private final PaymentRepository paymentRepo;
private final StripeClient stripeClient; // Платежный шлюз
private final StreamBridge streamBridge;
@Bean
public Consumer<TicketReservedEvent> chargePayment() {
return event -> {
try {
// 1. Локальная транзакция в своей БД
Payment payment = new Payment();
payment.setOrderId(event.getOrderId());
payment.setAmount(event.getAmount());
paymentRepo.save(payment);
// 2. Внешняя система (Stripe)
stripeClient.charge(payment.getAmount(), event.getCustomerId());
// 3. Успех
streamBridge.send(
"payment-events",
new PaymentChargedEvent(event.getOrderId())
);
} catch (Exception e) {
// 4. Ошибка → откат
streamBridge.send(
"payment-events",
new PaymentFailedEvent(event.getOrderId())
);
}
};
}
@Bean
public Consumer<TicketReservationFailedEvent> refundPayment() {
return event -> {
// Компенсирующая транзакция
Payment payment = paymentRepo.findByOrderId(event.getOrderId());
stripeClient.refund(payment.getAmount());
payment.setStatus("REFUNDED");
paymentRepo.save(payment);
};
}
}
2. Orchestration (Дирижер управляет)
Центральный Orchestrator (SAGA) управляет всеми шагами:
OrderSagaOrchestrator:
Step 1: Call TicketService.reserveTicket()
✓ Success → Step 2
❌ Fail → Compensate
Step 2: Call PaymentService.chargePayment()
✓ Success → Step 3
❌ Fail → Compensate Step 1 + Step 2
Step 3: Call NotificationService.sendEmail()
✓ Success → Done
❌ Fail → Compensate все
На Java с Temporal или Spring State Machine:
@Component
public class OrderSaga {
private final TicketServiceClient ticketService;
private final PaymentServiceClient paymentService;
private final NotificationServiceClient notificationService;
@Transactional
public void executeOrder(OrderRequest request) {
Order order = new Order();
order.setStatus("PENDING");
try {
// Шаг 1: Зарезервировать билет
TicketReservation ticket = ticketService.reserve(
request.getTicketId()
);
order.setTicketReservationId(ticket.getId());
// Шаг 2: Взять платеж
Payment payment = paymentService.charge(
request.getAmount(),
request.getPaymentMethod()
);
order.setPaymentId(payment.getId());
// Шаг 3: Отправить email
notificationService.sendConfirmation(request.getEmail());
order.setStatus("COMPLETED");
} catch (PaymentException e) {
// Компенсирующие транзакции (в обратном порядке)
ticketService.cancel(order.getTicketReservationId());
order.setStatus("FAILED_REFUNDED");
} catch (TicketException e) {
order.setStatus("FAILED");
} catch (Exception e) {
// Откатить всё
ticketService.cancel(order.getTicketReservationId());
paymentService.refund(order.getPaymentId());
notificationService.sendCancellation(request.getEmail());
order.setStatus("FAILED_COMPENSATED");
}
}
}
Choreography vs Orchestration
Choreography (Танец):
✓ Каждый сервис независим
✗ Сложно отследить логику (разреженная по всем сервисам)
✗ Сложно тестировать
✗ Cyclic dependencies возможны
Orchestration (Дирижер):
✓ Центральная логика (легко понять)
✓ Легче отследить ошибки
✓ Проще тестировать
✗ Зависимость от Orchestrator
✗ Single point of failure
Вызовы SAGA
- Идемпотентность — каждый вызов должен быть безопасен при повторении
// Если платеж упал но деньги ушли, повтор не должен взять еще раз
public PaymentResponse charge(String idempotencyKey, BigDecimal amount) {
// Проверяю: был ли уже такой платеж?
Payment existing = paymentRepo.findByIdempotencyKey(idempotencyKey);
if (existing != null) {
return existing.getResponse(); // Возвращаю старый результат
}
// Иначе новый платеж
}
- Временные ограничения — компенсирующая транзакция может не быть возможна
Если 30 дней прошли, не можно вернуть билет
→ Нужна сложная логика (деньги на счет, но не возврат)
- Monitoring — нужно отслеживать статус SAGA
Выводы
- SAGA = паттерн для распределенных транзакций
- Использует событие для координации
- Choreography когда сервисы независимы
- Orchestration когда нужна централизованная логика
- Требует идемпотентности и компенсирующих транзакций
- Отлично подходит для микросервисов и асинхронных процессов