Контекст
Существует расхожее мнение, что бизнес-логика в хранимках — это зло. Однако от опытных разработчиков нередко можно услышать, что это не так. Давайте разбираться.
- Первый подход — это использование хранимых процедур.
- Альтернативный подход — это объектно-реляционное отображение.
Есть две хороших статьи, описывающих проблематику: раз (оригинал) и два.
Первая описывает проблематику и хорошо показывает отличия систем с одним, двумя, тремя и N звеньями. В ней автор убедительно доказывает, что вся бизнес-логика должна быть в бизнес слое. Вместе с тем, автор также сообщает, что:
Нормальный язык разработки может быть использован для реализации бизнес правил. Такие языки более гибкие и более подходят для бизнес правил, чем SQL и хранимые процедуры.
По сути этим же утверждением автор приходит к мысли, что:
База данных не должна иметь функционального знания о том, что же из себя представляет покупатель в бизнес слое.
В комментариях к статье можно встретить и такое утверждение, вполне созвучное с утверждениями автора статьи:
Вообще SQL как язык удобен только для консоли, в программировании он не нужен.
На каком-то форуме встречал и такое эмоциональное высказывание (к сожалению не могу найти, чтобы добавить ссылку):
Только полный дурак будет хранить бизнес-логику в хранимках
Есть другое мнение. Автор второй статьи сообщает:
Я видел много разных ORM, но не видел ни одного хорошего. То есть такого, который за пределами простых примеров не превращается в обузу и прокрустово ложе
…
Я искренне извиняюсь перед теми братьями по профессии, кто вложил массу времени и усилий в создание многочисленных универсальных лучших в мире ORM. Мне правда очень жаль.
В его статье перечислены приёмы, позволяющие успешно применять первый подход.
Мы видим, что продолжают выходить инструменты, для систем с бизнес-логикой в хранимках. Например: Бизнес-логика в базе данных при помощи SchemaKeeper. Примечательна оговорка в начале статьи:
Статья не будет описывать преимущества или недостатки хранения бизнес-логики в базе данных. Предполагается, что выбор уже сделан читателем.
Другой автор в своей статье вполне обоснованно выступает в защиту данного подхода, описывая на примере развития сервиса платежей, из которого выпала работа пользователя:
Встает вопрос — зачем всю эту информацию откачивать из базы данных, обрабатывать и записывать обратно? Такой необходимости вообще нет, т.к. пользователь полностью исключен из автоматической обработки платежей. Соответственно, все это может решиться на уровне обработки данных, которым и занимается в полной мере сервер базы данных.
А ведь, то что мы раньше выделили бизнес-логику в объектной методологии нам совсем не пригодилось, т.к. нам стало нужно иметь просто одну хранимую процедуру, которая просто проведет платежное поручение из очереди. Да в этом случае бизнес-логика попадает в хранимые процедуры.
И задаёт резонный вопрос:
3-х звенная архитектура, предполагает, что бизнес-логика находится на сервере (среднее звено) и для данной задачи минует клиента. Но во-первых, мы ввели лишние звено, а во-вторых, оно снова же выполняет в данной задаче роль клиента — откачивает данные из базы данных, их обрабатывает и записывает назад. И все это только для того, чтобы было удобно на объектном языке обрабатывать бизнес-логику. Не велика ли цена?
Автор повторяет опыт с системой коммунальных платежей, когда бизнес-логика в хранимках снова выигрывает. От себя могу сразу также добавить пару примеров:
- Запуск/останов/отмена/перенос акций с вытеснением по приоритетам и/или смешиванием.
- Расчёт зарплаты с премиями, отпусками, штрафами и т.п.
Здесь, когда бизнес-логика в среднем звене, придётся вытянуть кучу данных, обработать, и, минуя пользователя, записать обратно. Для какой-нибудь чёрной пятницы, это будет довольно большая куча данных. Как следствие это будет работать гораздо дольше, чем могло бы. И с привлечением бОльших ресурсов.
Можно привести ещё массу примеров попроще, которые успешно решаются пакетными запросами и/или агрегированием на стороне БД. И нередко всё это с транзакциями. Вынос этого куда-либо является избыточным. Никто не спорит с тем, что первичная валидация и форматирование — должно происходить на клиенте.
Можно услышать такое возражение, в стиле — выкачивает много даннных? Делает работу, для которой создавалась БД? Не беда:
Вы в каком веке живете? Для того и развивается железо, чтобы прикладные программисты меньше думали о таких вещах и эффективнее решали бизнес-задачи, писали простой, легко сопровождаемый код.
Взять пример с акциями выше. Когда-то у нас расчёт акций происходил на сайте (CMS Bitrix24). После того, как этот механизм перенесли в БД, сайт удалось существенно разгрузить и сэкономить на хостинге — вполне значимую сумму денег.
Список возражений
Ниже собраны различные возражения к подходу "бизнес-логика в хранимках".
Повторяемость кода
Эта проблема проявляется при любом подходе. Решается переиспользованием кода. Результат одной хранимки может быть использован в другой хранимке.
Нельзя вести командную разработку
Это не так. Командная разработка строится абсолютно также, через контроль версий, slack и прочее.
Их сложно тестировать и следить за их изменением
Тут не совсем понятно — никто не запрещает писать тестовые скрипты. Изменения отслеживаются через Git.
Хранимые процедуры должны документироваться
Бесспорно, нужно просто это делать.
Сложность навигации в хранимках
Слабое возражение, так как в приложениях, использующих ORM подход, существует такое же большое количество классов. Кроме того хранимки структурируются схемами.
Трудности при переходе на другие СУБД
Да, это сравнимо с переписыванием клиентского приложения на другой язык (например было на PHP, стало на C#). Разные СУБД дают разные возможности. С другой стороны, за весь жизненный цикл проекта, это может и не потребоваться. Все случаи, которые изместны мне — это перевод серверов с ОС Windows на ОС Linux и следующие за этим перенос баз с MS SQL на PostreSQL. Однако даже Microsoft уже научили свою СУБД работать на Linux.
Другая возможная причина — это сэкономить на базе данных, используя одну из бесплатных. Это веский довод. Здесь всё зависит от необходимых возможностей. Решение потребуется принимать на старте проекта. Ровно так же, как это происходит с выбором языка для клиентского приложения.
СУБД требуют создавать пул подключений
Пул потребуется делать в любом случае, независимо от выбора подхода по месту размещения бизнес-логики. Суть в том, что при высокой нагрузке узким местом становится передача данных между клиентом и сервером БД. В этом случае можно экономить на подключениях к базе данных, выстраивая пул. Статьи на тему: раз, два и три.
SQL сложный
СУБД — это специализированный софт, максимально заточенный на работу с данными.
SQL — язык декларативный, а не императивный, алгоритмы какие-то редко бывают, отлаживать по шагам практически нечего. Требуется совсем другой подход к разработке. Для разработчика на императивных языках — это игра "на чужом поле". Если запрос чуть сложнее, чем тривиальный, то для грамотного его написания требуются специфические знания. Что можно делать в транзакциях, а чего нельзя? Когда индекс полезен, а когда — вреден? Что лучше, "IN" или "JOIN"? Почему происходит эскалация блокировок и на что это влияет? И так далее. Для этого потребуется кадровый разработчик именно по языкам SQL. Особой разницы в зарплате, по сравнению с другими разработчиками не замечено. Для работы с данными нужны соответствующие специалисты.
Зачем SQL-хранимки, когда есть Hibernate и его HQL?
- Хранимки изолируют данные и работу с ними от клиентского приложения. Для приложения остаются "действия" и оно ничего не знает о схемах и внутренней структуре базы данных. Через это обеспечивается уровень абстракции.
- Упрощается управление безопасностью. Это достигается путём отключения доступа ко всему, кроме хранимых процедур. Дальнейшая детализированная защита обеспечивается уже внутри хранимых процедур средствами языка и платформы.
- Хранимые процедуры компилируются и выполняются сервером БД сразу после вызова. Это ускоряет время работы содержащегося внутри кода.
- Замечено, что написание отличных от простой логики занимает меньше времени.
- Hibernate — это вариант адаптации инструмента обработки данных под свою область знаний.
- Можно не противопоставлять SQL-хранимки и Hibernate — хранимки нормально вписываются в Hibernate.
Итого
Спору нет, что бизнес-логика в хранимках — это шаг назад. Однако, этот шаг появляется не на пустом месте, а на основании критики существующего подхода. Конечно нельзя ударяться в другую крайность и поручать СУБД несвойственные ей задачи.
Я хотел бы закончить цитатой из статьи, уж очень она мне понравилась:
ORM – очень больная для нашей индустрии тема. В эпоху, когда облачный искусственный интеллект бороздит просторы квантового блокчейна, подавляющее большинство трудовых ресурсов занято прикручиванием бизнес-логики и пользовательского интерфейса к базам данных. Миллионы строк ужасного кода, забивание гвоздей микроскопами, боль и отчаяние повсеместно сопровождают этот творческий процесс. Один из корней этого печального положения дел — чрезвычайно стойкое заблуждение, что универсальный ORM в принципе возможен. А он невозможен, и этому есть фундаментальная причина, не подлежащая устранению. Осознание этого факта — первый шаг по направлению к выходу из этого кошмара. Выход — возможен, альтернативы — есть, но сначала — осознать, прочувствовать и научиться держать в фокусе внимания
Возможно всё-таки имеет смысл учесть опыт обоих подходов и, может быть, выработать какой-то новый.