> Как решать проблемы с транзакциями при изменении нескольких агрегатов в одной транзакции (Node.js, JavaScript)

Уровень: senior · Роль: backend · Категория: Технические вопросы

Компании: Mosline

Стек: Node.js, JavaScript

> Пример ответа

В распределённых системах и микросервисной архитектуре изменение нескольких агрегатов в одной транзакции - это антипаттерн. Вместо этого следует использовать сагу (saga) - последовательность локальных транзакций с компенсирующими действиями.Пример на Node.js:

JAVASCRIPT
// saga для создания заказа и списания средств
async function createOrderSaga(orderData) {
const sagaId = uuidv4();
const steps = [
{ name: 'createOrder', compensate: 'cancelOrder' },
{ name: 'reserveStock', compensate: 'releaseStock' },
{ name: 'chargePayment', compensate: 'refundPayment' },
];
try {
// Выполняем шаги последовательно
for (const step of steps) {
await executeStep(step.name, orderData);
await logSagaStep(sagaId, step.name, 'completed');
}
await logSaga(sagaId, 'success');
} catch (error) {
// Компенсация при ошибке
for (const step of steps.reverse()) {
try {
await compensateStep(step.compensate, orderData);
await logSagaStep(sagaId, step.name, 'compensated');
} catch (compError) {
console.error(`Compensation failed for ${step.name}:`, compError);
// Требуется ручное вмешательство
}
}
await logSaga(sagaId, 'failed');
throw error;
}
}

Ключевые принципы:

  • Каждый агрегат имеет свою транзакцию
  • Используйте идемпотентные операции (ключ идемпотентности)
  • Применяйте паттерн "Outbox" для надёжной отправки событий
  • Для простых случаев - двухфазная фиксация через координатор (например, с Redis или Kafka)

Альтернатива для монолита: если агрегаты находятся в одной БД, используйте ACID-транзакции с правильным уровнем изоляции (SERIALIZABLE) и блокировками, но это снижает производительность.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью