Например, перевод денег – это списание с одного счёта и зачисление на другой. Разместите оба действия в методе сервиса:
@Service
@RequiredArgsConstructor
public class TransferService {
private final AccountRepository accounts;
@Transactional
public void transfer(Long fromId, Long toId,
BigDecimal amount) {
var from = accounts.findById(fromId).orElseThrow();
var to = accounts.findById(toId).orElseThrow();
from.withdraw(amount);
to.deposit(amount);
}
}
Оба изменения фиксируются вместе. Если при зачислении возникнет исключение, вызывающее откат, списание тоже отменится.
Контроллеру остаётся проверить входные данные и вызвать сервис. Бизнес-правила находятся рядом с операцией, которую защищает транзакция.
Два нюанса:
- По умолчанию Spring откатывает транзакцию при `RuntimeException` и `Error`. Для checked exceptions можно настроить rollbackFor.
- В стандартном proxy-режиме вызов метода через `this` не активирует `@Transactional`.
Транзакция также не заменяет защиту от конкурентных изменений: для денежных операций отдельно продумайте блокировки или контроль версий.