Какие недостатки хранимых процедур заставляют переносить логику из БД в код приложения?

«Какие недостатки хранимых процедур заставляют переносить логику из БД в код приложения?» — вопрос из категории Базы данных, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Перенос бизнес-логики из хранимых процедур (Stored Procedures) в код приложения продиктован принципами современной разработки:

Недостаток хранимых процедур Последствие Решение в коде приложения
Сложность тестирования Невозможность изолированного unit-тестирования, зависимость от конкретной БД для интеграционных тестов. Логика покрывается модульными и интеграционными тестами с использованием in-memory БД или моков.
Нарушение инкапсуляции Распределение логики между БД и приложением, усложнение понимания системы. Четкое разделение: БД — хранение данных, приложение — бизнес-логика.
Вендорная привязка Синтаксис и возможности специфичны для СУБД (Oracle PL/SQL, PostgreSQL PL/pgSQL), что блокирует миграцию. Использование ORM (Hibernate) или SQL-мапперов (MyBatis) с диалектами позволяет легче менять БД.
Сложность CI/CD и контроля версий Хранимые процедуры часто выпадают из стандартного цикла сборки и системы контроля версий (Git). Код приложения полностью интегрирован в Git и pipeline сборки/деплоя.

Пример: Вместо вызова процедуры CALL transfer_funds(acc_from, acc_to, sum); логика явно описывается в сервисе:

@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
    Account from = accountRepository.findById(fromId).orElseThrow();
    Account to = accountRepository.findById(toId).orElseThrow();
    // Вся бизнес-логика (проверки, расчеты) здесь
    from.withdraw(amount);
    to.deposit(amount);
    accountRepository.saveAll(List.of(from, to));
    auditService.logTransaction(fromId, toId, amount); // Аудирование тоже в коде
}

Хранимые процедуры остаются оправданы для сложных аналитических отчетов или критичных по производительности массовых операций с данными.