Ответ
В микросервисах классические ACID-транзакции через границы сервисов невозможны. Вместо этого применяются паттерны BASE (Basically Available, Soft state, Eventually consistent) и следующие подходы:
1. Паттерн Saga: Последовательность локальных транзакций, где каждый следующий шаг зависит от успеха предыдущего. При неудаче выполняются компенсирующие транзакции.
// Пример реализации Saga с событиями
@Service
public class OrderSagaService {
@Transactional
public void createOrder(Order order) {
// 1. Локальная транзакция: создать заказ в статусе PENDING
orderRepository.save(order);
// 2. Отправить событие для резервирования товара
eventPublisher.publish(new ReserveItemsEvent(order.getId()));
}
// Обработчик успешного резервирования
@TransactionalEventListener
public void handleItemsReserved(ItemsReservedEvent event) {
// 3. Локальная транзакция: списать деньги
paymentService.charge(event.getOrderId());
// Отправить событие об успешном списании
}
// Компенсирующее действие при ошибке оплаты
@TransactionalEventListener
public void handlePaymentFailed(PaymentFailedEvent event) {
// Откат: освободить зарезервированные товары
inventoryService.releaseItems(event.getOrderId());
// Обновить статус заказа на FAILED
orderRepository.updateStatus(event.getOrderId(), OrderStatus.FAILED);
}
}
2. Паттерн Outbox: Гарантирует доставку событий в рамках локальной транзакции.
-- В рамках одной транзакции с бизнес-данными
BEGIN TRANSACTION;
INSERT INTO orders (id, amount) VALUES (123, 100.00);
INSERT INTO outbox (id, aggregate_id, event_type, payload)
VALUES (gen_random_uuid(), 123, 'OrderCreated', '{"amount":100}');
COMMIT;
-- Отдельный процесс (CDC, polling) читает outbox и публикует в брокер
3. Паттерн TCC (Try-Confirm-Cancel):
// Фаза Try: предварительное резервирование ресурсов
paymentService.reserve(orderId, amount); // Резервируем, но не списываем
inventoryService.reserve(orderId, items); // Резервируем товары
// Фаза Confirm: фиксация (идемпотентная)
paymentService.confirm(orderId); // Списание зарезервированных средств
inventoryService.confirm(orderId); // Подтверждение резерва
// Фаза Cancel: отмена (идемпотентная)
paymentService.cancel(orderId); // Отмена резерва
inventoryService.cancel(orderId); // Освобождение товаров
4. Технологии и инструменты:
- Брокеры сообщений: Kafka (с идемпотентным продюсером и exactly-once семантикой), RabbitMQ
- Оркестраторы: Camunda, Temporal, AWS Step Functions
- Мониторинг: Distributed tracing (Jaeger, Zipkin) для отслеживания потока транзакции
5. Критические требования:
- Идемпотентность: Все операции должны безопасно повторяться
- Компенсация: Для каждого действия должен быть определен компенсирующий action
- Мониторинг: Необходима видимость состояния распределенной транзакции
- Таймауты и ретраи: Стратегии обработки временных сбоев