Ответ
Перенос бизнес-логики из хранимых процедур (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); // Аудирование тоже в коде
}
Хранимые процедуры остаются оправданы для сложных аналитических отчетов или критичных по производительности массовых операций с данными.
Ответ 18+ 🔞
Да ты посмотри, что творят эти хранимые процедуры в базе! Ну реально, как будто в прошлом веке застряли, ей-богу.
Смотри, какие подводные камни всплывают, если всю логику в SQL-скрипты засунуть:
| В чём косяк хранимок | К чему это приводит | А как надо-то, по-человечески |
|---|---|---|
| Тестировать нихуя невозможно | Ну как ты это оттестируешь изолированно? Вечно нужна живая база, поднятая, настроенная — просто пиздец, а не разработка. | Всё в коде приложения: юнит-тесты на логику, интеграционные — с in-memory базой или моками. Красота! |
| Логика распидарашена по всем щелям | Половина — в приложении, половина — в недрах PostgreSQL или Oracle. Потом ищи-свищи, кто где что сломал. | Чёткое разделение: база — это склад данных, а приложение — это мозги, где вся бизнес-логика и живёт. |
| Привязанность к вендору намертво | Написал на PL/pgSQL — и всё, попал как кур в ощип. Захотел на другую СУБД переехать — переписывай всё, блядь, с нуля. | Используешь ORM типа Hibernate или MyBatis — они с диалектами работают, и миграция становится делом техники, а не героизма. |
| С контролем версий и CI/CD — полный атас | Эти процедуры часто в стороне от Git болтаются, деплоятся отдельными скриптами, в общем, бардак ебаный. | Код приложения — вся логика в Git, в одном пайплайне сборки и деплоя. Порядок, сука! |
Вот смотри, как это выглядит на практике:
Раньше вызывали какую-нибудь мутную процедуру: CALL transfer_funds(123, 456, 1000);. А что там внутри творится — хуй его знает, в недрах базы.
А теперь вся логика — вот она, прозрачная, перед тобой:
@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); // Аудирование тоже в коде
}
Красота же, ёпта! Всё видно, всё тестируется, всё в одном месте. Хранимые процедуры, конечно, не вымирают совсем — для тяжёлых аналитических отчётов или операций, где овердохуища данных гонять, они ещё могут быть полезны. Но основную-то логику — в код, блядь, в код! Там ей и место.