Назад к задачам
Junior — Middle+
6

Ревизия и улучшение функции денежного перевода на Kotlin

Компании, где спрашивали:

СамокатСамокат
Получайте помощь с лайвкодингом в реальном времени с Sobes Copilot
Условие задачи

В данном примере показана простая функция на Kotlin, осуществляющая перевод средств с одного счета на другой. Необходимо выявить потенциальные проблемы, связанные с транзакциями, и предложить способы её оптимизации или полной замены.

fun transfer(sourceAccountId: Long, destinationAccountId: Long, transferAmount: Int) {
    val sourceCurrentBalance = dao.getCurrentSum(sourceAccountId)
    dao.updateSum(sourceAccountId, sourceCurrentBalance - transferAmount)
    val destinationCurrentBalance = dao.getCurrentSum(destinationAccountId)
    dao.updateSum(destinationAccountId, destinationCurrentBalance + transferAmount)
}

Что может пойти не так?

  • Отсутствие проверки достаточности средств на исходном счёте → возможен отрицательный баланс.
  • Нет валидации входных параметров (отрицательная сумма, одинаковые идентификаторы).
  • Операции чтения и записи не находятся в одной атомарной транзакции → при конкурентных запросах может возникнуть состояние гонки и потеря средств.
  • Ошибки доступа к базе (исключения) не обрабатываются, что может привести к частичному выполнению операции.
  • Нет логирования и аудита действий, что усложняет отладку и мониторинг.

Варианты рефакторинга:

  1. Обернуть чтение и обновление балансов в единую транзакцию уровня SERIALIZABLE или использовать механизм optimistic locking.
  2. Добавить предварительные проверки: достаточность средств, корректность суммы, различие счетов.
  3. Ввести обработку исключений и откат транзакции в случае ошибки.
  4. Переписать функцию так, чтобы она возвращала результат операции (Result/Either) и/или бросала специализированные исключения.
  5. При необходимости вынести бизнес‑логику в отдельный сервис‑класс, а работу с базой оставить в DAO, что упростит тестирование (мок‑объекты).
  6. Добавить логирование операции и запись в аудиторскую таблицу.

Можно ли обойтись без этой функции? Если в системе уже реализован общий механизм финансовых операций (например, сервис платежей), то данную функцию следует удалить и использовать централизованный API, где уже реализованы все перечисленные проверки и транзакционность.