Pull to refresh

Где наша бизнес-логика для идеалиста?

Reading time 11 min
Views 25K
В этой статье я попробую сам разобраться в себе и в своих аргументах. Для начала попробую оппонировать автору статьи, перевод которой нашел на хабре Где наша бизнес-логика, сынок?. Её писал такой же идеалист, которым я был еще лет 10 назад. Поэтому по сути в этой статье я буду спорить сам с собой. Дело в том, что чем больше приложений я разрабатываю тем больше красивые теории перестают вписываться в идеальные схемы. Идеальные схемы хороши тем, что они просты. Вас спрашивают где бизнес слой? И ты легко можешь сказать на стороне клиента или на стороне сервера. Если смешенно многозначительно крутят носом и говорят «гавно-код». С этим я не согласен. Реальный мир не вкладывается в идеалистические концепции, точнее его можно туда запихнуть, но мы от этого скорее потеряем. Поэтому вначале подсознательно я понимал, что есть разные случаи. А теперь все более пытаюсь сформулировать, что влияет на то или иное решение по размещению бизнес логики. Здесь мы оставим красивые теории без аргументации молодым утопистам желающим простых решений.



Объектное и процедурное программирвоание в свете баз данных



Автор упомянутой статьи пишет:

… давайте четко определим: что же такое бизнес логика.… Сервер базы данных – это уровень хранения. Базы данных разработаны для хранения, получения и обновления данных с максимально высокой эффективностью.… Базе данных не должно быть дела до того, что такое покупатель, она должна заботиться только об элементах, используемых для хранения покупателя. У базы данных не должно быть возможности разобраться, какие таблицы должны хранить объект покупатель, и она должна работать с таблицами не обращая внимания на объект покупатель. Задача базы данных – хранить ряды в таблицах, которые описывают покупателя. Кроме базовых ограничений вроде каскадной целостности, типов данных, индексов и пустых значений, база данных не должна иметь функционального знания о том, что же из себя представляет покупатель в бизнес слое.


причем пишет об этом как о определении. А обосновывает только когда говорит о клиент-серверной архитектуре:

Достаточно быстро стало понятно, что можно сократить нагрузку на сеть и централизовать логику для уменьшения постоянных затрат на развертывание, перенеся большую часть бизнес логики на сервер. Архитектурно сервер был хорошо подготовленным местом в клиент-серверной системе, но база данных как платформа давала мало возможностей. Базы данных были спроектированы для хранения и выдачи и в их архитектуру не были заложены возможности расширения в направлении бизнес логики. Языки хранимых процедур в базах данных были разработаны для базовых преобразований данных, чтобы поддержать то, на что не хватало SQL. Языки хранимых процедур разработали для быстрого исполнения, а не для обслуживания сложных задач бизнес логики.

В большинстве случаев среднее звено существовало только для управления пулом соединений, но в некоторых случаях бизнес логика начала перемещаться в среднее звено потому, что языки разработки (C++, VB, Delphi, Java) гораздо лучше подходили для реализации бизнес логики, чем языки хранимых процедур. Вскоре стало очевидно, что среднее звено –это наилучшее место для бизнес логики.


Так и завязывается противоречие. Получается, что "бизнес логика начала перемещаться в среднее звено потому, что языки разработки (C++, VB, Delphi, Java) гораздо лучше подходили для реализации бизнес логики". Но какие характеристики упомянутых языков разработки лучше, чем процедурный язык SQL? Условия и циклы, разделение на процедуры есть и у SQL. Этим SQL ничем не отличается от любого процедурного языка, такого как C++, VB, Delphi, пока в действие не вступает объектная методология. Поэтому утверждение «языки хранимых процедур разработали для быстрого исполнения, а не для обслуживания сложных задач бизнес логики» очень поверхностное и достаточно спорное.

Язык SQL в части управления процедурной логикой ничем не отличается от других языков. Но действительно в нем не реализована объектная методология, которая позволяет управлять более сложной логикой. Поэтому отсюда будем утверждать, что если Вы не используете объектную методологию для управления бизнес логикой — вам должно быть все равно на чем писать алгоритмы — на SQL или Cи. А соответственно, совершенно без разницы будет ли на сервере находится т.н. средний слой скажем на Cи, который будет обращаться к базе данных, используя ее как хранилище, или же в базе данных будут хранимые процедуры SQL, которые так же будут обращаться к таблицам данных. Последние даже предпочтительнее, т.к. устраняет избыточный слой.

Но подчеркну, что все это справедливо только для не объектных языков. Когда же мы хотим получить преимущества от объектной методологии, вот только тогда мы и идем на то, чтобы данные в базе данных отделить от бизнес логики, и бизнес логикой управлять отдельно и на другом языке отличном от SQL.

Реляционная модель базы данных — это часть бизнес-логики



Когда автор статьи пишет "Сервер базы данных – это уровень хранения. Базы данных разработаны для хранения, получения и обновления данных с максимально высокой эффективностью.… Базе данных не должно быть дела до того, что такое покупатель, она должна заботиться только об элементах, используемых для хранения покупателя. У базы данных не должно быть возможности разобраться, какие таблицы должны хранить объект покупатель, и она должна работать с таблицами не обращая внимания на объект покупатель." — он не совсем точен. Сервер базы данных, конечно же выполняет задачу хранения данных, но это совсем не единственная его задача. На сервере базы данных также происходит обработка данных. Автор хочет свести обработку данных к элементарным операциям insert, update, delete над одной таблицей и сказать нам, что все остальное является бизнес-логикой, которую нужно отделить.

Но он забывает, что реалиционная модель данных, представляет данные в т.н. 3-ой нормальной форме, чтобы исключить избыточность хранения данных. Уже одно это говорит о том, что данные в базе данных не хранятся в одной таблице, а реляционно разложенны по набору таблиц. И совершенно не возможно обновить данные о «покупателе» используя только одну таблицу. Автор прямо не говорит о объектной методологии, но использует слово «объект покупатель». Думаю излишнее говорить, что объект покупатель прямо не соответствует таблице покупатели. Объект покупатель как правило выбирается сложным select`ом из набора связанных (join) таблиц. А как же он представляет обновление данных объекта покупатель? Не будет ли он в бизнес-логике с его ограничением на работу с одной таблицей на SQL заниматься логикой хранения реляционных данных? Именно такая логика хранения данных объекта покупателя в нескольких реляционных таблицах очевидно и должна быть реализованна в одной хранимой процедуре.

Еще мы должны договорится о том, что данные хранящиеся в таблицах базы данных это первичные данные, а то, что мы вытаскиваем из нее для обработки или для показа пользователю — это вторичные данные. Поэтому когда мы говорим о бизнес-логике, мы должны отдавать себе отчет, что когда мы разрабатывали реляционную модель данных мы уже задали часть правил бизнес-логики. Модель хранения данных уже накладывает определенные условия на бизнес-модель логики. С помощью хранимых процедур мы можем абстрагироваться от способа хранения данных, и тогда предоставить наружу данные в смысле объектов, а не реляционных таблиц. Тогда бизнес-логика будет работать со вторичными данными в объектной методологии.

В этих двух разделах я пока просто убрал перегибы, на которых настаивал автор, и думаю есть его последователи. Далее мы пойдем дальше и посмотрим в каких случаях эффективно, а в каких не эффективно использовать объектную методологию работы с бизнес-логикой.

Пошатнем мир идеалиста



С поправками сделанными выше я реализовал достаточно много приложений с разделением реляционной модели данных и объетной модели бизнес-логики. Но мой стройный мир один раз существенно пошатнулся.

Одно из приложений, которое я реализовывал, была реализация платежной системы в банках. По сути это одна форма, представляющая формуляр платежного поручения. Кроме всего прочего, этот форма умела осуществлять т.н. попутные конвертации. Причем конвертации могли быть различные: когда не хватает денег на платеж должно быть предложенно конвертировать из другой валюты, причем сумму и комисию за платеж можно конвертировать с разных валют, можно брать овердрафт на платеж, а тариф конвертировать, можно частично платить тем что имеешь, а не достающую сумму конвертировать ну и т.п. достаточно сложная бизнес-логика достаточно жестко переплетенная с интерфейсом пользователя.

Но один из заказчиков в один прекрасный день захотел, чтобы платежные поручения обрабатывались и проводились в системе автоматически, причем платежное поручение нужно было проверить на соответствие ряду требований, если не хватает денег также как и ранее в диалоге с банковским работником создать конвертацию или взять овердрафт, но в этот раз надо было исключить из этого процесса банковского работника полностью. Были установленны приоритеты валют — в каком поряке конвертировать, в каких случаях, что и как разрешенно, а в каких случаях выдавать на проверку банковскому работнику. Цель вполне понятна в 90% случаев банковский работник полностью разгружался от работы по верификации платежных поручений.

Итак, встал простой вопрос — в диалоге пользователя в 90% случаев отпала необходимость. Если предположить, что раньше мы хорошо отделили интерфес пользователя от бизнес-логики, то у нас уже реализована бизнес-логика в сложном наборе объектов. Проблема только в том, что инициация работы этой бизнес-логики ориентировалась на диалог пользователя. А сейчас нам надо было выбирать платежные поручения из базы данных, заменить логику инициации по диалогу пользователя другой логикой автоматической обработки, обработать уже имеющейся логикой, и записать проведенное платежное поручение обратно в базу данных. Причем проводка платежного поручения — это автоматическое создание баланса и еще одна сложная процедура. Причем не забыть еще конвертации, овердрафты, которые отражаются в балансе конечно отдельными документами и по другим статьям.

Встает вопрос — зачем всю эту информацию откачивать из базы данных, обрабатывать и записывать обратно? Такой необходимости вообще нет, т.к. пользователь полностью исключен из автоматической обработки платежей. Соответственно, все это может решиться на уровне обработки данных, которым и занимается в полной мере сервер базы данных.

А ведь, то что мы раньше выделили бизнес-логику в объектной методологии нам совсем не пригодилось, т.к. нам стало нужно иметь просто одну хранимую процедуру, которая просто проведет платежное поручение из очереди. Да в этом случае бизнес-логика попадает в хранимые процедуры.

Для клиент-серверной архитектуры альтернативы тут нет — это пример того как логика, если она была в клиенте необходимо мигрирует в сервер, и дублирование остается в клиенте, хотя логичнее было бы общую бизнес логику перенести на сервер в хранимую процедуру, оставив в клиенте только обсулуживание интерфейса пользователя.

3-х звенная архитектура, предполагает, что бизнес-логика находится на сервере (среднее звено) и для данной задачи минует клиента. Но во-первых, мы ввели лишние звено, а во-вторых, оно снова же выполняет в данной задаче роль клиента — откачивает данные из базы данных, их обрабатывает и записывает назад. И все это только для того, чтобы было удобно на объектном языке обрабатывать бизнес-логику. Не велика ли цена?

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

Теперь наученные тем, что бизнес-логика эффективна в базе данных логику формирования реального платежа на основании коммунального поручения мы осуществляли хранимой процедурой. Причем она использовалась как при создании коммунального поручения через интерфейс, а также как API к внешним (например, бухгалтерским) программам по отношению к банковской системе, а так же как прием коммунальных поручений через интернет-банк. Было ли это возможно если бы бизнес-логика была бы не в хранимой процедуре, а например в объектах на C#? Да, возможно, но путем введения совершенно излишнего среднего звена с дублированием методов получения/записи данных в базу данных.

Но когда мы доходим до пакетов, бизнес-логика диктует нам необходимость отбирать платежи по определнным признакам, проставлять одинаковые признаки (такие как принадлежность пакету, статус, текстовое пояснение пользователю) сразу большому набору платежей, осуществлять подсчет общей суммы и количества платежей в пакете используя функции SUM(), COUNT()… и я бы очень удивился если все это было бы реализованно в объектно ориентированной методологии, а не с использвоанием SQL. И когда в заключении после формирования пакета надо сформировать сводный платеж, и не уметь его сформировать из SQL — это разве достоинство того, что бизнес-логика находится не в базе данных?

Этих двух примеров думаю достаточно, чтобы понять что нахождение бизнес-логики в хранимых процедурах базы данных более чем оправдано, а в ряде случаев даже необходимо.

И дело в том, что по сути существует два рода бизнес-логики. Одна бизнес-логика определяет то, как взаимодействовать с пользователем, а другая относится к групповой обработке данных. В первом случае, действительно удобно на объектном языке управлять бизнес-логикой на клиенте или среднем звене, а во-втором, при автоматической, групповой обработке данных имеет смысл работать над первичными данными, а не над вторичными, не отгружая их предварительно из базы данных и не записывая назад.

Другая крайняя точка зрения



Если автор обсуждаемой статьи высказывает (на мой взгляд не достаточно обоснованно) одну крайнию точку зрения, согласно которой в базе данных не должно быть бизнес-логики. То можно высказать и другую крайнию точку зрения, согласно которой вся бизнес-логика может находится в базе данных. Единственно чего мы при этом лишаемся — это отсутствие объектной методологии в работе с бизнес-логикой.

В каких случаях это может быть оправдано?

Не так давно я писал о своем стартапе по созданию браузерной игры. И там действительно используется именно такой подход — вся бизнес-логика в базе данных. Чем это вызвано?

Веб-разработка, все таки достаточно специфична — клиентом является браузер и управляется всегда другим скриптовым языком (JavaScript). А задача сервера сводится к формированию HTML страниц для клиента. В этой связи у нас пропадает возможность управлять клиентом с помощью объектно-ориентированного языка (если только не считать JavaScript — объектным языком, которым он в полной мере не является). Соответственно, пропадает основной стимул вытаскивать данные из базы, чтобы создавать бизнес-модель вне базы в клиенте.

Почти тоже самое и на сервере. Тут конечно мы не лишены объектной методологии, если например, используем ASP.NET. Но задача сервера сводится к формированию HTML — страниц. А точнее к наполнению HTML — страниц данными, которые получаются хранимыми процедурами как правило простым select- запросом. Да, конечно еще остаются реакции на нажатие кнопок и клики на check-боксы и т.п. Но в отличии от офисных приложений с формами, где осуществляется достаточно сложная логика взаимодействия с пользователем, веб-страницы крайне упрощены, т.к. реакция как правило сводится к формированию другой HTML — страницы с другими данными.

В голом остатке, в веб-приложениях остаются задачи, которые выполняются «за кадром». К примеру, в браузерной игре (экономической стратегии с элементами РПГ), которую я реализовываю — это такие задачи, как проверка процесса производства по созданию продуктов, расчет характеристик рынка (цен покупки), осуществление закупок, выборы в гильдиях, осуществление долгосрочных поставок и т.п. Все они выполняются с определенной переодичностью в игре, они составляют 60-80% всей бизнес-логики, и все они происходят «за кадром» игрока. И было бы странно, если бы пришлось для осуществления этих задач вытаскивать первичные данные и представлять их в виде объектной модели, только затем, чтобы записать эти данные после обработки обратно. Выборки данных (select-запросы) так или иначе реализуются хранимыми процедурами.

Остаются лишь реакции на нажатия кнопок, и прочие взаимосвязи с интерфейсом. Но для JavaScripta опять все сводится к получению порции данных, и написания клиентских алгоритмов, которые вряд ли можно назвать бизнес-логикой, это скорее логика отображения интерфейса. А оставлять бизнес-логику, которая будет обслуживать интерактив работы с пользователем через HTML — страницы в объектной модели, но из-за простоты этого интерактива это скорее накладно, чем дает какие -то преимущества.

Вот и получается, что для веб приложений практически вся бизнес-логика находится в базе данных. Это опять же позволяет независимо работать ядру игры в базе данных, не зависимо от работы веб-сервера. Позволяет писать любые другие клиенты, обращаясь к хранимым процедурам как к API. Минус остается лишь в одном — не очень удобно без объектного языка реализовывать бизнес-логику, но так как часто это логика работы не с одним объектом, а опять же это групповые операции над многими сущностями в базе данных, то не все так однозначно.

Других архитектурных минусов, наличия бизнес-логики в базе данных попросту нет.
Tags:
Hubs:
+4
Comments 37
Comments Comments 37

Articles