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

    В этой статье я попробую сам разобраться в себе и в своих аргументах. Для начала попробую оппонировать автору статьи, перевод которой нашел на хабре Где наша бизнес-логика, сынок?. Её писал такой же идеалист, которым я был еще лет 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. Минус остается лишь в одном — не очень удобно без объектного языка реализовывать бизнес-логику, но так как часто это логика работы не с одним объектом, а опять же это групповые операции над многими сущностями в базе данных, то не все так однозначно.

    Других архитектурных минусов, наличия бизнес-логики в базе данных попросту нет.
    Ads

    Comments 37

      0
      С вашей статьей согласен, спасибо.

      Сам раньше подумывал о неком «db specific ORM», где можно было бы создавать слой работы с данными посредством хранимых процедур, представлений и прочих специфичных для той или иной СУБД конструкций (воспользоваться преимуществами каждой из них), а затем этот слой (точнее объектную надстройку над ним) использовать в объектной модели, в бизнесс логики. И (как было замечено) когда мы сохраняем данные о клиенте в БД — это совершенно не значит, что все они будут вставлены в одну плоскую таблицу Clients, но сам бизнесс слой об этом ничего не знает, и логику хранения можно изменять как угодно без его ведома.

      Но хочу добавить, что плюс бизнес логики на стороне приложении (а не в БД) это возможность более легкого и простого масштабирования (распараллеливания). И думаю многие разработчики и команды сознательно идут на шаг полного игнорирования всех фич выбранной СУБД, и забивают ее каким-либо ORM'ом. Да, можно потерять в скорости, но никто не мешает поставить еще одну (две, три ...) машины рядом в помощь. Проще и быстрее.

      Как и было сказано в этой статье, все зависит от конкретного приложения (и только от него) и условий его разработки.
        +1
        Все очень сильно зависит от типа приложений, которые вы разрабатываете. Если это «справочники» с набором CRUD операций, то лепить объектный слой над ними большого смысла нет. А если приложение автоматизирует деятельность в какой-то предметной области, то без доменной модели и слоев абстракций вы очень скоро придете к несопровождаемому коду.

        Раздел «Пошатнем мир идеалиста» вызвал у меня боль чуть ниже спины. API в виде хранимой процедуры и обоснование дублирования логики избавлением от лишних операций — вы в каком веке живете? Для того и развивается железо, чтобы прикладные программисты меньше думали о таких вещах и эффективнее решали бизнес-задачи, писали простой, легко сопровождаемый код.
          0
          Вы что-то напутали, я нигде не обосновывал дублировние логики.
            0
            В разделе «Пошатнем мир идеалиста» я ставлю под вопрос необходимость объектно ориентированной модели в случае, когда нет необходимости в интерфейсе пользователя. Если Вы прочитали не так, попробуйте перечитать.
              +1
              Так и прочитал. Но в приведенном вами примере было сказано, что интерфейс пользователя уже был. Следовательно, должен был быть слой логики. Т.е. приложение уже имело информацию об объектах. Для реализации логики проводок в базе вы должны были неизбежно продублировать ее там. Теперь, при изменении упомянутой вами логики конвертации, придется вносить правки в двух местах, логически не связанных.
                0
                Вот теперь все так. Но какой вывод? Изначально бизнес-логика оказалась не там, и если бы она была в базе данных этого дублирования удалось бы избежать.
            0
            Да и какое-то у вас противоречие, есть желание " эффективнее решать бизнес-задачи, писать простой, легко сопровождаемый код", и в то же время не хотим задумываться о «избавлении от лишних операций». Не находите это странным?
              +1
              Нет, не нахожу. Еще раз: за избавление от некоторого количества операций ввода-вывода вы платите размытием логики между слоями, потерей механизмов абстракции в коде, отсутствием возможности использования современных инструментов и технологий разработки.
                0
                продублирую: «за избавление от некоторого количества операций ввода-вывода вы платите размытием логики между слоями» — это доведение до абсурда, тогда как «размытие локиги» — это только в головах разработчиков, а плата в виде «опираций ввода/вывода информации из базы» вполне ощутима.

                Что же касается «отсутствием возможности использования современных инструментов и технологий разработки» — тут вы не правы. Если Вы внимательно читали, и понимаете под этим объектно ориентированную методологию, то я как раз написал в каких случая оно не нужно, и наоборо выгодно уступает языку SQL.

                Ну и да, лишнии слои абстраций нужно не вводить всегда и везде, а использовать их только тогда когда они нужны. Для групповых операций SQL лучше работает на реляционных данных, чем на объектной абстракции.
                  0
                  Можно еще уточнить, что это не просто «избавление от некоторого количества операций ввода-вывода», это еще замена в определенных случаях удобного языка SQL на работу с объектами. Например, чтобы заменить update по таблице с определенными условиями, нужно вводить цикл с условиями. Мне совершенно не кажется, что применение менее удобного здесь механизма объектного языка («современных инструментов и технологий разработки»), лучше чем применение SQL для того, для чего он предназначен.
                    0
                    Да и потом, как Вы представляете себе «операции ввода» в описанном выше случае? заменить один update множеством update по каждому объекту под внешней транзакцией? Причем по одному полю, как вы это сделаете: на каждое поле введете хранимую процедуру, в одной хранимой процедуре будите проверять какое поле поданно для изменения, а какие оставить как есть, или вообще вынесите SQL из хранимой процедуры в объектный язык? И это того стоит? Ради чего?
            0
            Лично мне сложно ориентироваться в БД где больше сотни хранимок и несколько десятков функций.
            Часто происходит дублирование логики, что приводит к сложностям при внесении изменений.

            Может быть автор подскажет как он решает данные проблемы.
              0
              С другой стороны, возможна и обратная ситуация: одну и ту же БД могут использовать несколько разных приложений, на разных языках (например, складской учет + веб-морда), и легко может возникнуть дублирование кода.
                0
                А код с более чем сотней классов вас при этом не смущает? Я согласен, что ООП позволяет лучше структурировать код. Ну скажем в нашем приложении более 2000 sql процедур, разбираться во всех их обычно никому не нужно. Как правило на отдельную задачу требуется набор не более, чем 5-10 sql-процедур, а это совсем не много, если еще как и все прочие нормально именовать и не забывать комментировать.
                0
                Мне кажется сосредоточить всю бизнес-логику в одном слое в принципе невозможно. Всё-равно она постепенно размазывается. Простой пример, имеем требование (вполне себе бизнес-требование): «поле Email контакта должно быть не пустым, уникальным и содержать валидный email-адрес».
                Первым делом в базе данных добавляем на поле constraint not null, уникальный индекс, check constraint (validation rule) с проверкой валидности email. Вся логика в базе данных, хорошо. Но у гас есть веб-формочка, пользователям явно не понравится ждать после сабмита только для того, чтобы выяснить, что они забыли заполнить Email. Дублируем требование, добавляем проверку на не пустоту и валидность на JavaScript. Требование на уникальность на этом уровне реализовать затруднительно. Аналогичные проверки нужны на сервере приложения, мы же не можем доверять проверкам на клиенте. Итого, одно бизнес-правило размазалось по всем слоям с разным уровнем детализации.
                  0
                  1. Может, я что-то упустил, но как вы в итоге получаете конечную объектную модель для ее отображения, если БД, насколько я помню, в большинстве своем умеют отдавать только одно- и двухмерные данные? Скажем, та же сводная страница покупателя, на которой нужны ФИО, адрес, список его заказов с подробным перечнем товаров, пересчетами валют и скидками, использованные купоны… Фантазия заказчика, к сожалению, безгранична. Вы их как-то упаковываете в таблицы или достаете кучей разных запросов, а потом сшиваете в клиенте/middleware? Не было ситуаций, когда для «сшивки» данных все равно требовались какие-то бизнес-правила? Хотя ими тоже можно напрячь базу данных… Но тогда не возникнет ли противоречие вашей стратегии оптимизации (не использовать второй слой для того, что умеет сама БД), ибо базе придется либо формировать сложные результирующие данные, либо кешировать промежуточные запросы (если вы получаете данные несколькими заходами-процедурами), либо таки дублировать их.
                  2. ИМХО, бизнес-логику выносят в отдельный слой (tier, если угодно) с идеей упрощения архитектуры — «БД умеет только CRUD и больше ничего, клиент умеет только получать и дооформлять данные (например, через XSLT — особо тяжелых вычислений на клиенте быть не должно), а также формировать запросы, а центральный сервер умеет только парсить запросы клиента, дергать БД, что-то вычислять и формировать конечный отклик». Те же сортировки-группировки-курсоры можно считать частью бизнес-логики, лежащей на центральном сервере, а БД воспринимать исключительно как интерпретатор сложных запросов, а не как самостоятельный «мозговой центр» (по сути, между «дай мне всю таблицу» и «по хитрожопому отJOINь десять таблиц и прогруппируй по двадцати столбцам в случайном порядке» никакой разницы — что БД сказали делать, то она и сделает без особой инициативы).
                    0
                    1. Конечно должен использовать второй вариант: «формировать сложные результирующие данные». Но в это определенно нет ни каких бизнес-правил, т.е. такого нет «для «сшивки» данных все равно требовались какие-то бизнес-правила». Единственно, конечно group и order можно считать бизнес-правилами, но им там и место.

                    2. Ну, вот эта идея и не правильна в корне, если используется как истина в последней инстанции. В статье как раз указаны случаи, когда это крайне излишно и не удобно для реализации. Выполнение каких то принципов лишь для того, чтобы выполнить или как мне выше написали «за избавление от некоторого количества операций ввода-вывода вы платите размытием логики между слоями» — это доведение до абсурда, тогда как «размытие локиги» — это только в головах разработчиков, а плата в виде «опираций ввода/вывода информации из базы» вполне ощутима.
                      0
                      Вы так и не ответили на вопрос, как вы тянете результаты работы бизнес-логики из базы данных.

                      Ситуация: есть некий интернет-магазин. В нем есть товары. У товаров, как ни странно, есть цены.
                      • Во-первых, эти цены бывают в разных валютах — нужно знать текущие курсы и уметь их пересчитывать (как для отображения в каталоге, так и для расчета суммы заказа).
                      • Во-вторых, существуют скидки на определенные группы товаров — их также нужно отображать в каталоге и учитывать при суммировании. Скидки, кстати, бывают не только "-20%", но и «купи две — третья бесплатно». Уже хорошо как для алгоритма, так и для администратора интернет-магазина (он не полный идиот и не будет переписывать каждый раз сложные процедуры — он настроит скидки в админке, а система сгенерирует SQL для процедуры сама).
                      • В-третьих, бывают хитрые условия доставки и оплаты a-la «доставка от 2500 рублей бесплатна, но при доставке за МКАД оплачивается каждый километр; также учитывается вес заказа, подъем на этаж и фаза луны».
                      • В-четвертых, бывает, что налоги рассчитываются из разных «точек» — от суммы цен товаров, от суммы цен товаров с учетом скидок, от суммы заказа с учетом предыдущих налогов...
                      • Самое прикольное (в моем списке) — в-пятых: товары могут иметь различные настраиваемые параметры, от которых также может зависеть их цена (цвет, комплектация etc). Весело, не так ли?

                      И все бы хорошо, и иногда действительно нужно запихнуть бизнес-логику в базу данных — например, без пересчетов валют и скидок не будет работать сортировка товаров по цене. Однако как из описанной мною выше ситуации из базы данных вытянуть информацию о заказе, которая должна включать в себя все товары, их обычные цены, цены со скидками (мы любим рисовать зачеркнутые цены), выбранные параметры товаров (их может быть сколько угодно — хоть ноль, хоть один, хоть 30), выбранные способы доставки/оплаты, все промежуточные точки расчета групповых скидок, налогов и итоговую сумму? Результат запроса-то у нас двухмерный. Будете городить стремные конструкции из диких JOIN и UNION, чтобы все уместить в одно отношение, или же разобьете запрос на несколько маленьких, и они будут дублировать части друг друга (например, отдельно список товаров и их параметры, отдельно сумма этого же списка + скидки/налоги + итог)?
                        0
                        Я Вам ответил «Конечно должен использовать второй вариант: «формировать сложные результирующие данные»». Т.е. да буду использовать JOIN и UNION. Но при вашем уточнении могу уточнить, все зависит как это мы будем отображать. В вашем примере, я сомниваюсь, что все параметры отображаются сразу. Как правило, пользователю вначале показывается список из сокращенных параметров но по всем товарам, а потом при выборе товара показывается более подробная информация по одному товару. Это позволит разгрузить пользователя от детальной информации при выборе, и дать детально когда выбран уже товар. Тогда это два select`а.
                          0
                          Я вам дам пример одного простенького select`a из моей игры, получение состояния персонажа (думаю подобные вопросы отпадут):

                          	select att1.Value as Health, att2.Value as Energy, att3.Value as Cheerfulness, 
                          			att4.Value as Intelligence, att5.Value as Money, 
                          			att19.Value as TKomfort,
                          			c.Name as CityName, 
                          			CONCAT(' (', c.TemperatureAir, ' °C)') as TemperatureAir,
                          			a.CityID, c.X as CityX, c.Y as CityY, c.AllowPort as AgentCityIsPort,
                          			a.ArrivalTime, 
                          			IF (a.CityIDDestination <> 0, c2.Name, '') as CityNameDestination,
                          			IF(a.ArrivalTime > SYSDATE(), 
                          				TIME_TO_SEC(TIME(SUBTIME(a.ArrivalTime, SYSDATE()))), 0) as RemainingTime,
                          			GetGameSec() as GameSec, GetGameDate() as GameDate,
                          			a.OrganizationID as OrgID, a.ProfessionID as ProfID, p.Name as ProfName, 
                          			IF (a.ProfessionID IN (2, 4), g.Name, IF (a.ProfessionID IN (3, 5), o.Name, 
                          					IF (a.ProfessionID IN (6, 7), m.Name, '')) ) as OrgName, 
                          			a.AccessType, a.VoteForAgentID, a2.Name as VoteForAgentName, 
                          			IF (a.ProfessionID = 5, o.Tax, 0) as GuildTax, 
                          			IFNULL(pm.Mode, 0) as PlanMode, a.IsDead, 
                          			a.FightID, a.FightStep,
                          
                          			IF (exists (select * from Stock where AgentID = locAgentID and CityID = a.CityID), 1, 0) as IsStock,
                          
                          			IFNULL(t1.Level, 1) as HouseL, IFNULL(t2.Level, 1) as WorkshopL,
                          			IFNULL(t3.Level, 1) as FarmL, IFNULL(t4.Level, 1) as ClinicL,
                          			IFNULL(t5.Level, 1) as TrafficL, IFNULL(t6.Level, 1) as ArmoryL,
                          			IFNULL(t7.Level, 1) as ForestL, IFNULL(t8.Level, 1) as RiverL,
                          			IFNULL(t9.Level, 1) as FieldL, IFNULL(t10.Level, 1) as HillL,
                          
                          			ca.ImageName as CoatsOfArmsName, a.AgentType, a.Name as AgentName
                          
                          
                          	from Agents as a
                          		join Cities as c on c.ID = a.CityID
                          		left join Cities as c2 on c2.ID = a.CityIDDestination
                          		join Professions as p on p.ID = a.ProfessionID
                          		left join GuildOfMerchants as g on g.ID = a.OrganizationID
                          		left join OrderOfKnights as o on o.ID = a.OrganizationID
                          		left join Manufactory as m on m.ID = a.OrganizationID
                          		left join Agents as a2 on a2.ID = a.VoteForAgentID
                          		join AgentAttribute as att1 on att1.AttributeID = 1 and att1.AgentID = locAgentID
                          		join AgentAttribute as att2 on att2.AttributeID = 2 and att2.AgentID = locAgentID
                          		join AgentAttribute as att3 on att3.AttributeID = 3 and att3.AgentID = locAgentID
                          		join AgentAttribute as att4 on att4.AttributeID = 4 and att4.AgentID = locAgentID
                          		join AgentAttribute as att5 on att5.AttributeID = 5 and att5.AgentID = locAgentID
                          		join AgentAttribute as att19 on att19.AttributeID = 19 and att19.AgentID = locAgentID
                          		left join PlansMode as pm on pm.AgentID = a.ID
                          		left join coats_of_arms as ca on ca.ID = a.CoatsOfArmsID
                          
                          		left join AgentTerritory as t1 on t1.AgentID = a.ID and t1.CityID = a.CityID and t1.TerritoryID = 8
                          		left join AgentTerritory as t2 on t2.AgentID = a.ID and t2.CityID = a.CityID and t2.TerritoryID = 6
                          		left join AgentTerritory as t3 on t3.AgentID = a.ID and t3.CityID = a.CityID and t3.TerritoryID = 5
                          		left join AgentTerritory as t4 on t4.AgentID = a.ID and t4.CityID = a.CityID and t4.TerritoryID = 7
                          		left join AgentTerritory as t5 on t5.AgentID = a.ID and t5.CityID = a.CityID and t5.TerritoryID = 25
                          		left join AgentTerritory as t6 on t6.AgentID = a.ID and t6.CityID = a.CityID and t6.TerritoryID = 9
                          
                          		left join AgentTerritory as t7 on t7.AgentID = a.ID and t7.CityID = a.CityID and t7.TerritoryID = 1
                          		left join AgentTerritory as t8 on t8.AgentID = a.ID and t8.CityID = a.CityID and t8.TerritoryID = 4
                          		left join AgentTerritory as t9 on t9.AgentID = a.ID and t9.CityID = a.CityID and t9.TerritoryID = 3
                          		left join AgentTerritory as t10 on t10.AgentID = a.ID and t10.CityID = a.CityID and t10.TerritoryID = 2
                          
                          	where a.ID = locAgentID;
                          
                          
                            0
                            У Вас есть альтернатива этому select`у? (и заметим в скобках, в этом select`е нет ни грамма бизнес-логики)
                              0
                              Никто не говорил об альтернативах — я просто уточнял вашу идею (^_-) И, кстати, даже с точки зрения «база данных только для CRUD» оно не кажется из ряда вон выходящим (ИМХО, конечно). Хотя, на мой взгляд, CONCAT(' (', c.TemperatureAir, ' °C)') — ерунда, потому что отображение около числа надписи "°C" или «в градусах Цельсия» (и уж тем более заключение значения в скобки) — задача только представления и ничья больше. Даже бизнес-логика оперирует данными, такими, как «5» или «значение в градусах Цельсия» (последнее скорее как абстрактная идея, значение enum, отвечающего за шкалу, нежели как конкретное высказывание).
                                0
                                ну придрались к мелочи :) Ваше замечание имеет смысл только тогда, когда тем-ра участвует в вычислениях (в данном случае на JavaScripte), в моем же случае там это не происходит. А лепить лешего ради какой-то умозрительной чистоты — я естественно не буду, т.к. это просто излишнее.
                                  0
                                  в этом же селекте есть ровно обратный пример: c.X as CityX, c.Y as CityY — координаты города. Они используются в вычислениях, чтобы отрисовать город на карте, и в тоже время координаты города отображаются игроку в формате (x-y). Если бы нужно было бы только второе, вы увидили бы еще один CONCAT. Поэтому все по необходимости. И это не бизнес-логика, просто все зависит какой объект нужно получить на стороне клиента. А объект создается из требований задачи.
                            0
                            Кстати, плата за «размытие логики» = оплата разработчикам лишних трудовых часов, необходимых для «вспоминания» архитектуры и всех нюансов, сделанных ради скорости работы всей системы. Вопросы, обозначенные в посте, обычно поднимаются на крупных проектах, с которыми работают весьма объемные команды с неплохой почасовой ставкой (не наши детсадовские 1000-2000$). Да, опыта и личных качеств им не занимать (иначе бы не платили), но все-таки это люди, и им нужно входить в контекст, в поток. Сравнима ли стоимость подобных оптимизаций с точки зрения оплаты труда (это я еще не считаю другие расходы) со стоимостью медленной работы конечной системы? Или вы всю жизнь занимались разработкой биржевых экономических систем и игровых движков, где скорость работы крайне критична?
                              0
                              «плата за «размытие логики» = оплата разработчикам лишних трудовых часов, необходимых для «вспоминания» архитектуры и всех нюансов, сделанных ради скорости работы всей системы.»

                              Я хочу подчеркнуть, что это делается как раз не ради скорости работы системы (хотя и это тоже), а для ускорения разработки и более простого сопровождения потом. А «вспоминания» архитектуры — происходит всегда не зависимо от того, какая эта логика. Это даже не «размытие логики» — это просто написание логики с использованием разных средств.

                              Более того, логику в базе данных и на объектном языке вполне могу делать разные разработчики.
                                0
                                Ускорение разработки? Это вы про то, что разработчику приходится переключаться между языком высокого уровня и процедурами SQL, разнящимися от базы к базе? Или про то, что ценой этапа синхронизации модулей программы из разных сред (Java и Oracle DB разные же среды, не так ли?), который, опять же, стоит дороже синхронизации модулей из одной среды (проще отладить два куска Java, чем Java и интерпретатор запросов/процедур SQL-базы), можно распараллелить разработку (интересно, что мешает двум разработчикам писать на одном языке)?

                                Кстати, вы с таким подходом с миграциями и версионностью разрабатываемых систем работали? Миграция таблиц и данных в них — уже целое приключение, а хранимые процедуры и сложные вьюхи…
                                  0
                                  Было бы очень страно, если бы одно приложение использовало бы разные базы данных. Поэтому «разнящимися от базы к базе» — это совсем мимо.

                                  А «переключаться между языком высокого уровня и процедурами SQL» — это просто не серьезно. Обычно просто пишется слой на SQL, а потом на объектном языке, если второе вообще требуется. А без первого вы вообще не напишите ни одно приложение, поэтому так или иначе будите переключаться, что совсем кстати не сложно.

                                  Ну и потом альтернативы то у вас нет :)
                                    0
                                    Не поддерживали вы студенческие недоделки, ой не поддерживали… Вы бы не только разные БД в одной системе увидели. Хотя я имел в виду «одну систему написать с использованием SQL Server, другую на Оракле, третью на MySQL, четвертую на SQLite, PostgreSQL...» — повторюсь, есть разные заказчики и разные требования, и иногда слова «тут неуместна SQL Server, и мне все равно, что вы пять лет назад купили лицензию, и поэтому должны ее отработать» могут стоить проекта тысяч на 500. Впрочем, против такого идеализма я ничего не имею.
                                      0
                                      Ну тут Вы правы, я привык к тому, что мы диктуем минимальные требования к установке системы (в том числе и в плане лицензий), а не нам заказчик.
                                        0
                                        Просто это аналогично тому, что мы написали ПО на C#, а заказчик захотел чтобы она была на Java :) Перевести можно, но почему то считается, что PL-SQL должен почему то легко (или автоматически?) переводится на T-SQL
                                          0
                                          Хотя, я конечно не совсем точен. Даже в моей игрушке две базы — MySQL и SQL Server Express, но у них разные задачи. SQL Server Express используется для авторизации пользователя, т.к. ASP.NET несколько автоматизирует этот процесс, ну и потом оказалось удобно держать аккаунты отдельно от логики игры. Но эти базы практически не пересекаются.
                                        0
                                        Последние не касается этой темы ни как.
                                          0
                                          Если только как «миграции не рассматривались в рамках темы данного поста». Выбор места для бизнес-логики — всего лишь одна из кучи архитектурных задач, и работать им все равно придется в тандеме.
                                            0
                                            Я просто не вижу взаимосвязанности этих вопросов.
                                +2
                                Наверно подводя некий итог, я хочу сказать, точнее еще раз сделать на этом акцент, что существует разная бизнес-логика. Есть бизнес-логика вполне себе замкнутая в себе, которую удобно реализовывать с помощью SQL. И есть другая бизнес-логика, связанная с управлением интерфейса пользователя. Эта логика порой сильно сложнее в плане реализации, и тут без объектной методологии не обойтись.

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

                                  Для вашего приложения. Но им, к счастью, мир веб-приложений не исчерпывается.

                                  А задача сервера сводится к формированию HTML страниц для клиента.

                                  У вас просто сервер простой, вот и все.

                                  Only users with full accounts can post comments. Log in, please.