Пример бизнес-кейса, где важна атомарность транзакции
Что проверяют
Отличаете ли атомарность от остальных букв ACID и можете ли назвать кейс, где частичное выполнение реально вредит. Дальше почти всегда идёт продолжение про границы: что делать, когда часть операции уходит во внешнюю систему.
Как отвечать
Атомарность это «всё или ничего»: либо применились все изменения операции, либо ни одного. Понятные кейсы: перевод между счетами (списание и зачисление), оформление заказа (заказ, резерв товара, списание бонусов), смена тарифа со списанием платы. Логика везде одна: промежуточное состояние видно быть не должно, иначе деньги или товар пропадают.
В Java одна бизнес-операция это одна транзакция:
@Transactional
public void transfer(long from, long to, BigDecimal amount) {
accounts.withdraw(from, amount);
accounts.deposit(to, amount);
outbox.save(new TransferCompleted(from, to, amount));
}Дальше добавьте то, чего атомарность не даёт. От одновременного изменения той же строки она не спасает, это изоляция: нужен select ... for update или версия строки. Внешний вызов (платёжный шлюз, письмо, сообщение в брокер) откатить нельзя, поэтому в той же транзакции пишут запись в таблицу исходящих, а отправляет её отдельный процесс с повторами. Где шагов несколько и они в разных сервисах, атомарности не будет вовсе, будет сага с компенсациями и идемпотентность по ключу операции.
Где спотыкаются
Вызывают транзакционный метод изнутри того же класса. Прокси в этом случае не участвует, аннотация молча не работает, и частичные изменения коммитятся. То же самое с приватными методами.
Считают, что откат отменит письмо или запрос в соседний сервис. Внешний эффект уже произошёл. Отправку либо переносят в таблицу исходящих, либо делают после коммита, и всё равно с защитой от повтора.
Ловят исключение внутри транзакции и продолжают. Транзакция уже помечена на откат, коммит упадёт с UnexpectedRollbackException. Обратный случай тоже частый: проверяемое исключение не откатывает транзакцию, пока не указан rollbackFor.