Почему не стоит использовать двухуровневую архитектуру при разработке клиент-серверных приложений  



Почему не стоит использовать двухуровневую архитектуру при разработке клиент-серверных приложений +10

Поблагодарим моего знакомого за то что он рассказал мне о своей новой лабораторной работе, в университете, по дисциплине связанной с базами данных. В противном случае статья просто бы не увидела бы света.

Сутью данной лабораторной работы было написать n-ое количество валидаций, хранимых процедур и триггеров в MS SQL. Все триггеры, валидации и хранимые процедуры очень просты, но это лишь первая лабораторная и дальше будет хуже, будут многоэтажные sql запросы и хранимые процедуры очень большого объёма.

Как мне кажется тратить время на изучение подобных вещей в 2018 году очень не практично, а именно то что никто не использует просто голую базу данных и сразу вывод на клиент. Все приложения имеют бизнес логику и подвергаются постоянному масштабированию и изменению. И завязывать всё на базе данных не правильно так как может возникнуть ряд проблем которые невозможно решить используя лишь СУБД в качестве серверной стороны.

Примерами таких проблем могут быть:

  • Возникновение необходимости использования другой базы данных
  • Необходимость вывода данных в нескольких видах для разных клиентских сторон(JSON, HTML, XML и др)
  • Добавление и изменение таблиц, колонок или изменения их имён, что повлечёт за собой изменение всего связанного с каждой таблицей или колонкой
  • Необходимость получения данных так же из других баз данных установленных на сервер, либо вообще получение данных из других сервисов

Рассмотрим типичный пример того что нам нужно отправить письмо по email


Как это выглядит если отправлять сообщение через MS SQL

DECLARE @tableHTML  NVARCHAR(MAX) ;  

SET @tableHTML =  
    N'<H1>Work Order Report</H1>' +  
    N'<table border="1">' +  
    N'<tr><th>Work Order ID</th><th>Product ID</th>' +  
    N'<th>Name</th><th>Order Qty</th><th>Due Date</th>' +  
    N'<th>Expected Revenue</th></tr>' +  
    CAST ( ( SELECT td = wo.WorkOrderID,       '',  
                    td = p.ProductID, '',  
                    td = p.Name, '',  
                    td = wo.OrderQty, '',  
                    td = wo.DueDate, '',  
                    td = (p.ListPrice - p.StandardCost) * wo.OrderQty  
              FROM AdventureWorks.Production.WorkOrder as wo  
              JOIN AdventureWorks.Production.Product AS p  
              ON wo.ProductID = p.ProductID  
              WHERE DueDate > '2004-04-30'  
                AND DATEDIFF(dd, '2004-04-30', DueDate) < 2   
              ORDER BY DueDate ASC,  
                       (p.ListPrice - p.StandardCost) * wo.OrderQty DESC  
              FOR XML PATH('tr'), TYPE   
    ) AS NVARCHAR(MAX) ) +  
    N'</table>' ;  

EXEC msdb.dbo.sp_send_dbmail @recipients='yourfriend@Adventure-Works.com',  
    @subject = 'Work Order List',  
    @body = @tableHTML,  
    @body_format = 'HTML' ;  


Прекрасный код, согласитесь?

Редактирование и изменение данного кода будет доставлять нам анальные боли неудобства. А это лишь простая табличка, в которой нет стилей и ничего сложного, просто текст. А теперь представим что мы в реальном мире и нам надо отправлять нормальные, красивые сообщения, с картинками, вложениями и стилями. Перейдя по ссылке, вы собственными глазами увидите какими могут быть большими html шаблоны писем. И редактирование чего либо подобного будет тратить тонну вашего времени и сил.

А в случае если бы вы использовали трехуровневую архитектуру, вам бы не пришлось использовать столь изощрённые методы оформления писем. Был бы использован какой либо фреймворк с шаблонизатором, и дело с концом.

Усложненный функционал


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

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

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

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

  • Поменялось название поля в таблице — вам нужно будет просто описать в модели, что данное свойство теперь ссылается на другое поле, и вам не нужно будет менять логику приложения и все места где используется данное поле.
  • Нужно использовать данные из другой СУБД — просто делаете запрос в другую БД и спокойно работаете с этими данными.
  • Нужно обратиться к стороннему сервису и получить данные от туда перед тем как делать к примеру INSERT — не беда, это очень просто сделать в любом фреймворке.
  • Нужна асинхронность при выполнении запросов — берём тот же NodeJS с его расхваленной ансинхронностью и дело в шляпе.
  • Надо поменять базу данных — вам не придётся переписывать все хранимые процедуры для новой БД, вы просто смените драйвер базы данных и забудете об этом.

И таких примеров тысячи, если не десятки тысяч.

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

Эта статья призывает вас, развиваться в правильном направлении и изучать более продвинутые вещи, ведь с каждым годом функционал приложений становятся всё более и более сложным и продвинутым. Сейчас современные фреймворки все SQL запросы напишут за вас. И не пропустят точку с запятой. От вас же требуется грамотно использовать все те инструменты которые предоставляет фреймворк, а не придумывать велосипеды.

Так же хотелось бы чтобы преподаватели ВУЗов смотрели на реалии в которые попадает студент на работе, и давали реальные задачи, а не задачи валидации мобильного телефона на уровне БД.

UPD 13.02.2018
Поблагодарим speshuric за прекрасно аргументированный комментарий к данной статье

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (114):

  1. bindlock
    /#10665418

    Кто-то вообще так делает, как было в примере? Это же безумие.

  2. SirEdvin
    /#10665438 / +2

    Поменялось название поля в таблице — вам нужно будет просто описать в модели, что данное свойство теперь ссылается на другое поле, и вам не нужно будет менять логику приложения и все места где используется данное поле.

    По факту, надо. Иначе какой смысл переименования то?


    Нужно использовать данные из другой СУБД — просто делаете запрос в другую БД и спокойно работаете с этими данными.

    Так же можно сделать и в кастомных функциях некоторых баз данных.


    Нужно обратиться к стороннему сервису и получить данные от туда перед тем как делать к примеру INSERT — не беда, это очень просто сделать в любом фреймворке.

    И в базе данных так же можно сделать.


    Нужна асинхронность при выполнении запросов — берём тот же NodeJS с его расхваленной ансинхронностью и дело в шляпе.

    Но ведь какая разница, если база не асинхронна и мы говорим все равно про базу. Даже если клиент асинхронный, база все равно будет синхронно обрабатывать запросы.


    Надо поменять базу данных — вам не придётся переписывать все хранимые процедуры для новой БД, вы просто смените драйвер базы данных и забудете об этом.

    Нет. Вы смените драйвер и будете рефакторить кучу кода с вопросами "а не использую ли я фичи из старой бд", потому что разница бывает значительна.


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


    1. Очень бедный инструментарий, где на каждый чих вам надо будет придумывать свой инструмент. Деплой, обновление, различные проверки и тесты и так далее.
    2. Большинство разработчиков довольно плохо знают базу данных на таком уровне, что бы писать качественный код, так как опять же, первый пункт привел к большему абстрагированию от баз данных, что привело к тому, что разработчики более детально знают язык, а не базы данных.

    • MaximChistov
      /#10665466 / -1

      0. Отсутствие контроля версий для кода в хранимых процедурах

      • dezconnect
        /#10665468

        Расскажите это ребятам из Zalando.

      • SirEdvin
        /#10665578

        Справедливости ради, вы не совсем правы. Никто не мешает вам хранить исходный код процедур в файлах скриптов и уже делать им версионирование через git, так же как вы делаете это для других языков программирования.

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

        • VolCh
          /#10666012

          Да, в теории в целом никто не мешает. Но на практике очень часто мешают сами базы данных, которые не имеют такой публичной сущности как исходный код процедуры. Они ожидают императивных команд на создание, удаление или изменение процедур.

          • RPG18
            /#10666188

            которые не имеют такой публичной сущности как исходный код процедуры

            Если ваша субд это не позволяет, то не следует говорить за все остальные и особенно " в теории в целом никто не мешает"

            • VolCh
              /#10666200

              Популярные SQL базы не позволяют.

              • RPG18
                /#10666402

                PostgreSQL популярная база или нет? `CREATE OR REPLACE` вполне декларативный способ замены кода. Получить код процедуры то же не проблема.

                • VolCh
                  /#10666706

                  Это императивная команда "создать или заменить".

                  • RPG18
                    /#10666714

                    А как должна выглядеть не императивная команда на декларативном SQL?

                    • VolCh
                      /#10666860

                      Декларативность SQL сильно преувеличена, имхо.

          • nsinreal
            /#10666294

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

            Тем не менее, для выбранной вами бд вполне может существовать уже готовый тулинг, который умеет подобное.

            • vlivyur
              /#10666572

              Мы перешли на создание пустой процедуры, если её не было, а потом банальный ALTER PROCEDURE. В этом случае ещё и дата создания (а не последнего обновления) остаётся в БД.

    • moresnow
      /#10667934

      Обязательно должен быть изоляционный слой между приложением и базой. Каким именно он будет даже не очень важно, хранимые процедуры, энтити фреймворк и т.п. в большинстве случаев я предпочитаю процедуры и слой через который приложение(я) с ним работают. При сравнительно небольшой сложности — EF.
      В противном случае, даже при удачной архитектуре БД, при увеличении базы до террабайтов и сотен миллионов записей потаблично, возникают проблеммы тюнинга которые очень сложно, а при не очень удачной архитектуре, просто невозможно решить. Решать вопросы перформанса БД должен ДБА, а не заизолировав базы от приложения мы заметно затрудняем задачи оптимизации.
      Вот прямо сейчас смотрю на проприетарное приложение/фреймворк в финансах, который не использует изоляцию (гоняют SQL прямо из приложения). БД под 2 ТБ и несколько сильно нагруженных таблиц с > 100млн записей.
      Стоимость железа под это с учетом высокой доступности и необходимой скорости работы эксорбитальна, а облачные решения с учётом всех требований ещё дороже (сравнительно небольшая компания с ~$20B в системе).
      Критерий скорости — процедуры по закрытию финансового и подготовки к следующему дню должны закончится до 07:00 начавшись в 20:00 и на сегодня, с учётом самого тяжелого транзакционного дня и при отсутствии проблем остаётся всего 2 часа в запасе, а реально, с учетом некоторой специфики, ещё меньше.

      Была продемонстрирована версия с изоляцией (хранимками) и возможностью легкой оптимизации базы без изменения клиента, на ночных операциях укладывающаяся в 2 часа вместо 11ти на одинаковом железе. Такой гигантский скачёк конечно же не «гениальность» оптимизаторов, а _исключительная_ тупизна разработчиков, как фреймовка так и клиента на этом фреймворке построенном. При более-менее удачной архитектуре в другом проекте удалось поднять производительность примерно в 2 раза и прозрачно перевести базы в облако снизив затраты на инфаструктуру ДБ примерно на 1/3.

      • lexxpavlov
        /#10667968 / +1

        >сравнительно небольшая компания с ~$20B
        а где найти портал в вашу вселенную, где компания с 20 миллиардами — небольшая?

        • moresnow
          /#10669316

          B ипотечном бизнесе $20B это сравнительно небольшая сумма

  3. Ernado
    /#10665492

    Сдается мне, вы все свалили в кучу.
    Во-первых, в реальном мире, подобный подход мне мне ни разу не доводилось видеть ни в одном проекте, за все то время, что я занимаюсь разработкой.
    Во-вторых, именно для лабораторной работы, чтобы разобраться со всеми возможностями БД — такой пример очень даже хорошо работает.

    • Hackerook
      /#10665644

      Я и хочу продемонстрировать людям, то что не стоит тратить время на подобные методы углубления в БД. Ибо есть куда более важные вещи которые стоят внимания, и если 1/1000 студентов обучающихся по подобной программе вдруг решат стать спецами в базах данных, то это их право, но основной массе стоит давать то, что сейчас очень востребовано на рынке.
      Чтобы при выходе с ВУЗа студент мог сразу устроится на нормальную работу, а не иметь очень большое количество знаний, которые ему скорее всего не пригодятся, и при трудоустройстве практически с нуля обучаться тому что потребуется в работе.
      Собственно как вы и сказали:

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

      • areht
        /#10665830

        > Чтобы при выходе с ВУЗа студент мог сразу устроится на нормальную работу,

        нет, это требования к ПТУ. Именно там люди, не желающие учиться лишнему, должны получать рабочие навыки.

        (что не оправдывает ни странные программы ВУЗов, ни деградацию ПТУ)

        • akryukov
          /#10665906

          нет, это требования к ПТУ. Именно там люди, не желающие учиться лишнему, должны получать рабочие навыки.

          Любопытно узнать, какие у вас требования к ВУЗам. Им не обязательно готовить студентов к устройству на нормальную работу?

          • areht
            /#10665920

            Любопытно узнать, что вы называете ненормальной работой?

            • akryukov
              /#10665960

              Я нормальной считаю такую, где есть ДМС со стоматологией и стабильность в проекте. Но это не важно в контексте вопроса. Я интересовался вашим субъективным мнением о нише выпускников ВУЗов на рынке труда.

              • areht
                /#10666056

                Я вот контекст и уточняю.
                Имею заметить, что во-первых, что ощущаю противоречие в том, что «дополнительное» страхование называют определяющим «норму» для работы.
                Во-вторых, не очень понятно кто же будет делать нестабильные проекты.
                В третьих, ПТУ вполне способно подготовить специалистов, принимающих как должное ДМС и стабильность в проекте.

                А вообще aamonster правильно сформулировал, только с поправкой, что во всяких госкомпаниях программисты на cobol вполне успешно игнорируют быструю смену технологий.

                Отвечая на ваш вопрос, ниша выпускников ВУЗов на рынке труда исходя из вашего контекста — от $300 000 в год и доля в компании.

              • RPG18
                /#10666198

                Вы не поверите, но Питерский метрополитен и водоканал имеют свои хорошие клиники включая стоматологию.

        • aamonster
          /#10665966

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

      • bormotov
        /#10665850

        > не стоит тратить время на подобные методы углубления в БД.

        что бы понять «стоит» или «не стоит» нужны критерии оценки. Плюс, традиционный вопрос в любом разговоре о цене: мы покупаем или продаем?

        > есть куда более важные вещи которые стоят внимания

        кроме способа оценки нужны метрики важности

        > основной массе стоит давать то, что сейчас очень востребовано на рынке.

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

        > не иметь очень большое количество знаний, которые ему скорее всего не пригодятся

        что бы не иметь «лишних знаний», нужно больше уделять внимание базовым дисциплинам (так, кто сказал «матан» и тихонько хихикает?) и концепции, практику воспринимать именно как способ закрепить эти самые базовые знания.




        мне, например, регулярно приходится смотреть в сотни тысяч строк кода на Oracle PL/SQL, который до сих пор пишут. Среди основной команды разработки есть светлые головы, пишут они вполне себе современны код, насколько им позволяет инструмент и характер хадачи.

        Как-то в одном из подкастов обсуждали подходы к legacy, и человек сказал «за более чем 10 лет практики разработки софта, только один раз была возможность начать проект, всё остальное — работа с legacy». Помните об этом. 90% всего, что нужно — уже написано.

        Что бы эффективно выкидывать код написаный не в рамках концепции, которая вам нравится, нужно уметь хорошо такой код не только читать, но и понимать.

        В конце концов, PL/SQL — это просто еще один язык.
        У того же Оракла, например, есть ОО-расширение в PL/SQL. Подозреваю, что для кого-то это окажется открытием. Еще, например, есть ключевое слово deterministic, подсказывает компилятору, что функция чистая (привет, ФП!), и её можно использовать для построения индексов.

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

      • Lolialoneathome
        /#10665870

        Но умение писать сложные запросы, хранимки и триггеры — не углубление в БД, а скорее поверхностное знакомство с ней. К тому же, насколько мне известно, в вузах дают не только базы данных, а еще какой-нибудь курс по C++/C#/Java/etc. И хотя чистый sql не выглядит достаточно востребованым на рынке, я слабо представляю себе собеседование на бекендера без вопросов по sql.
        Архитектура такая скорее всего сейчас есть разве что в каких-то очень старых монструозных системах, но имхо в универе в качестве учебного примера — ок.

        • akryukov
          /#10665948

          Стоит держать в голове, что средний выпускник устраивается на junior бэкендера.
          А это значит, что основная масса вопросов к нему будет по основам БД. Например: Как выполнять базовые операции в БД? Чем левый джойн отличается от правого? Какие нормальные формы он знает?
          Вопросы про хранимки и триггеры будут скорее к разработчику уровня middle. И контекст будет "А что лучше применять для решения прикладной задачи X?".
          Поправьте меня, если вы часто собеседуете соискателей и спрашиваете что-то существенно другое

          • VolCh
            /#10666034

            Как по мне, то вуз должен выпускать разработчика уровня middle+. Это вопрос к качеству вуза, если приходится спрашивать то, что у выпускника уже подтверждено наличием диплома. Грубо, если я на собеседовании начинаю задавать такие вопросы, значит наличие диплома для меня ничего не значит. Такие же вопросы я будут задавать человеку, собеседующемуся на джуна без опыта работы и формального образования.

    • bormotov
      /#10665828

      видимо, вы очень мало занимаетесь разработкой.

    • Zordhauer
      /#10665932

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

    • akryukov
      /#10665962

      2) Рассказ про хранимые процедуры, триггеры и т.п. в учебном заведении без пояснения их назначения — это безобидные факты, которые "сдал и забыл". Настоящим злом оно становится, если двухзвенную архитектуру подают как единственно правильную (или просто единственную) и больше ничего не рассказывают.

  4. NikitOS9
    /#10665624

    oracle apex

  5. kostey2204
    /#10665636

    Здесь вы в основном описываете как делать не следует.
    Хотелось бы увидеть пример того как надо делать. (если можно на примере десктопного .net-приложения для работы с БД SQL)
    Вы дважды упомянули трёхуровневую архитектуру, разъясните, пожалуйста, как она будет выглядеть в этом случае?

    • bormotov
      /#10665866

      «Как нужно делать» — сильно зависит от целей. От целей того, кто делает.

      Например, есть цель «заработать денег», а за написание вот такого кода платят больше — то нужно писать такой вот код.

      Хотя, конечно, можно «продать начальству» другой подход, показав на примерах, что если писать код по-другому, можно заработать еще больше денег.

    • VolCh
      /#10666042

      1. База данных. Обычно тупое хранилище данных с возможностью сложных выборок
      2. Клиент. Обеспечивает UI и соблюдение простейших правил ввода типа "поле не должно быть пустым"
      3. Сервер приложений. Содержит основную бизнес-логику и контролирует работу всего приложения.

      • vlivyur
        /#10666568

        3.1 Исполнитель сервера приложений слился, исходников нет, надо поменять = на <> в бизнес-логике.

        • VolCh
          /#10666710

          Декомпиляция и правка. Но и в случае СУБД подобное может быть.

          • vlivyur
            /#10667166

            При том, что даже с исходниками порой не знают что делать. С СУБД обычно худо-бедно умеют работать, отчёты многие сами клепают.

            • VolCh
              /#10667196

              А не сталкивались с обфускацией кода процедур в СУБД? Если брать какой-то продукт как чёрный ящик, то и относиться к нему нужно как к чёрному ящику.

              • vlivyur
                /#10668356

                Нет, не сталкивался. Только WITH ENCRYPTION и то один раз и пока без надобности лазить в него.

        • areht
          /#10667226 / +1

          > исходников нет

          Зачем так жить? Если уж у вас с исполнителем такие отношения, что он может и исходники стереть, и логическую бомбу подложить — эту проблему техническими средствами решать не очень перспективно.

          • vlivyur
            /#10668352

            Это может быть коробочный продукт, типа 1С, только не такой гибкий, с допилом под требования.

            • areht
              /#10668406

              Тогда не понял вашу мысль.

              Если вы заказчик — доставайте кошелёк на допил под требования.
              Если исполнитель — у вас есть исходники.
              Если вы хотите хакнуть чужой коробочный продукт — инструментарий тоже понятен, но едва ли ради вас архитектуру на двузвенку поменяют.

              • vlivyur
                /#10668414

                Слился исполнитель, некуда кошелёк доставать. А так бы можно было внутри SQL-процедуры всё поменять на нормальном SQL, вместо ковыряния в машкодах.

                • areht
                  /#10668972

                  Ну да, это вариант

                  > Если вы хотите хакнуть чужой коробочный продукт — инструментарий тоже понятен, но едва ли ради вас архитектуру на двузвенку поменяют.

                  Вы или заранее планируете последствия и заказываете продукт с удобной вам архитектурой (или исходниками, что опять же проще), или покупаете as is у фрилансера-однодневки подешевле и потом уже мучаетесь с результатом как можете.

                  Лично я не видел спроса на «сделайте нам двузвенку, а то вдруг вы исчезните с исходниками...»

                  • vlivyur
                    /#10669108

                    Лично я не раз наблюдал/говорил «О, всё на процедурах. Отлично!». А двузвенка/трёхзвенка тут вообще никаким боком.

                    • areht
                      /#10670220

                      > Лично я не раз наблюдал/говорил «О, всё на процедурах. Отлично!».

                      Там ключевое — "заранее планируете и заказываете". А кто там что после этого говорит — дело пятнадцатое.

  6. Zordhauer
    /#10665954

    Кажется, автор забыл о том, что аналитические операции лучше всего делать именно на уровне СУБД, иначе будет большая проблема с производительностью.
    Банальный пример:


    1. вытянуть все данные из бд и обработать уже в коде — получаем огромную нагрузку из-за передачи больших объемов данных (это скажется на отклике даже не смотря на передачу внутри сервера)
    2. обработать все данные встроенными в СУБД механизмами, приспособленными и оптимизированными под подобные задачи, и в приложении подучить лишь результат.

    И к вопросу о возможной смене БД: даже без использования хранимых процедур это, как писали выше, будет болезненно, но за счёт многоуровневой архитектуры часть функционала, нереализуемого в новой субд, можно будет реализовать программной прослойкой, чтоб со стороны основного кода это не отличалось от работы с прошлой субд.


    Резюмируя: если вам вдруг кажется, что какой-то существующий подход или какая-то небезызвестная технология не нужны или даже вредны, то скорее всего вы просто пытаетесь ее применить не по назначению)
    Если пытаться чистить зубы лопатой, то тож может показаться, что лопата как-то уж совсем неудобный инструмент…

    • eyellow
      /#10665986

      Соглашусь с вами во всём. Специально залогинился, чтобы написать подобный комментарий, однако вот же — уже написан :)
      Немало я видел систем, где заявлялась «БД-независимость», «лёгкая смена БД» и всё такое… Но, во-первых, в большинстве случаев это не надо. Во-вторых, в серьёзных системах всегда есть завязка на конкретную БД в виде использования специфичных особенностей для повышения производительности, надёжности всей системы. Ну нельзя так просто взять и сменить базу данных.
      Чем ближе код к данным, тем быстрее вся связка будет работать. И если система основана на обработке данных, т.е. главное у вас — это данные, то написание бизнес-логики на хранимых процедурах вполне хороший и жизнеспособный вариант.
      Соглашусь, что варить почту в хранимке — глупость. Но, ребята, этим и не надо заниматься в этом месте, пусть это делает какой-нибудь сервер приложений. А хранимые процедуры пусть, к примеру, кладут тысячи проводок в секунду, обрабатывают транзакции и делают всякий биллинг в кровавом энтерпрайзе.

    • nsinreal
      /#10666348 / -1

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

    • michael_vostrikov
      /#10666856

      3. Вытянуть из БД частично сгруппированные данные несколькими простыми запросами, и сделать более сложную обработку в коде.

    • moresnow
      /#10669360

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

      Согласен, но с одним исключением, внезапно для репортов попадающих под SLA понадобилось парсить LOB поле (text, varchar(max)), в таблице с >100млн записей (реальный случай).
      До написания элегантного костыля — единственным способом было тащить всё из базы и парсить на апп сервере. Костыль включал в себя добавление поля в таблицу + некоторый допил клиента для записи в это поле и отдельный одноразовый парсер для популяции этого поля в исторических записях. Тригер не выход, ибо на конкретной таблице любой тригер приоводит к заметному падению производительности

  7. VolCh
    /#10666076

    Бизнес-логика в современном мире имеет полное право жить в современных СУБД, с одной стороны, а, с другой, есть два "вечных" холивара:


    • допустимо ли размазывать бизнес-логику между СУБД и серверу-приложения и(или) толстому клиенту
    • допустимо ли размещать в СУБД презентационную и инфраструктурную логику приложения, имеющую бизнес-ценность, типа отправки html-писем клиентам. Как по мне, то подобную отправку можно делать только в рамках решения административных технических задач. То есть администратор базы данных может написать какой-то триггер, который будет отсылать ему какое-то письмо при каких-то условиях. Может написать хранимку, которая будет вызываться по расписанию и, например, пересчитывать материализованные представления по закрытию дня и т. п. Но не более.

  8. Vest
    /#10666094

    Проходил я мимо вот такого софта: http://www.risingroad.com/foxhound/index.html


    Его создатель написал всё это на одной единственной БД без серверной части в виде отдельно стоящего Java/nodejs/итд. сервера, и, да, на SQL. Посмотрите на всякие скриншоты, вы удивитесь, но вся «шаблонизация» — это конкатенация строк в SQL. Я не говорю про то, что он им зарабатывает.

  9. QuickJoey
    /#10666148

    В моём понимании, бизнес-логика, это нечто, что соблюдается очень жёстко. Следовательно, оно должно быть на уровне базы данных. То есть никто, даже случайно, не должен иметь возможность сделать данные противоречивыми.
    Примитивный пример такой логики: при заполнении ВШГ в товарной номенклатуре, напишем триггер, который будет вычислять объём товара, и количество штук этого товара в кубе, а вычисленные поля класть сюда же, в номенклатуру. Вопрос, зачем это тянуть наружу и вычислять там? А если клиенты написаны под несколько платформ, на каждой эти вычисления писать? А если кто-то полез руками напрямую в таблицу исправлять ВШГ?

    • VolCh
      /#10666380

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

      • QuickJoey
        /#10666912

        С одной стороны да, с другой стороны, при достаточном опыте, уже хватает ума в такую жёсткую логику класть только то, что не имеет исключений.
        p. s. Клуб анонимных любителей залезть в данные напрямую :-)

    • Hackerook
      /#10666422

      А если клиенты написаны под несколько платформ, на каждой эти вычисления писать?

      Я не говорю делать вычисления на клиенте, я говорю делать дополнительную прослойку на серверной стороне которая будет отделять клиентскую часть от базы данных. В ней и писать все вычисления.

      А если кто-то полез руками напрямую в таблицу исправлять ВШГ?

      Это не совсем хорошая практика исправлять что-либо подключаясь напрямую к базе данных, для избежания каких-либо проблем используют такие вещи как «seed» к примеру, и с помощью этого инструментария и вносят изменения в БД.

      • QuickJoey
        /#10666896

        Я не говорю делать вычисления на клиенте, я говорю делать дополнительную прослойку на серверной стороне которая будет отделять клиентскую часть от базы данных. В ней и писать все вычисления.

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

        Это не совсем хорошая практика исправлять что-либо подключаясь напрямую к базе данных, для избежания каких-либо проблем используют такие вещи как «seed» к примеру, и с помощью этого инструментария и вносят изменения в БД.


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

        • VolCh
          /#10667144 / -1

          Первичный ключ идёт из коробки.

    • SirEdvin
      /#10666432

      Вопрос, зачем это тянуть наружу и вычислять там?

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

      • QuickJoey
        /#10666880

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

        • Hackerook
          /#10667120

          Вы кажется не совсем понимаете что такое клиентская часть, а что такое серверная часть.
          Представим ситуацию в которой необходимо сделать некий сервис по учёту книг к примеру, и реализовать отслеживание этого учёта на IOS, Android, Веб и Дестктоп, вот это всё что я перечислил, является клиентской частью.
          В качестве базы данных будет к примеру postgresql или mysql — это уровень данных
          И есть так называемый backend который к примеру реализован на PHP фреймворке Laravel в виде REST API.
          И вам не нужно будет логику описывать 50 раз в каждом из приложений, вы всю логику опишете на уровне API. И единственное что вы будете делать на стороне клиента (IOS, Android, Веб и Дестктоп) это просто правильный вывод и отображение информации с её предварительным получением из API.
          Послали запрос на добавление новой книги к примеру. API обработало это, так как нужно и выдало в том формате в котором запросило соответствующее клиентское приложение.
          В этом и вся суть трёхуровневой архитектуры, а то что вы описали выше это двухуровневая.

          • RPG18
            /#10667146

            единственное что вы будете делать на стороне клиента (IOS, Android, Веб и Дестктоп) это просто правильный вывод и отображение информации с её предварительным получением из API.

            Нужно еще написать удобное api на конкретном языке программирования и покрыть это тестами. Да же несмотря на это, тут не без своих трудностей. Чем больше бизнес сущностный и операций над ними, тем сложнее делать внешнее API.

            • VolCh
              /#10667208

              По сути вам и так, и так придётся его делать на конкретном языке программирования, будь то Transact SQL, C# или JavaScript. И тестами покрывать надо и то, и другое, и многое другое. Просто в случае *SQL экосистема развита меньше, но есть плюс в виде отсутствия (в идеале) межпроцессного взаимодействия между хранилищем и бизнес-логикой.

              • RPG18
                /#10667232

                Тут экономие на написание прослойки в виде API, т.к. для вызова хранимки достаточно использовать имеющийся драйвер базы данных.

                • VolCh
                  /#10667252

                  Ну, в некоторых ситуациях (веб, например), придётся писать прослойку, только чтобы обернуть этот драйвер в, например, HTTP. Пускай тупую, просто принимающую готовый запрос и отправляющей ответ, но обёртку.

                • nsinreal
                  /#10667718

                  Никакой реальной экономии нет на долгосроке. В краткосроке может быть.

                  В самом тупом варианте вы напишите http-обертку над вызовом хранимки за достаточно малое время. Проценторно в долгосроке затраты на такую прослойку будут стремиться к нулю.

                  В более умном варианте вы используете ООП/ФП, фреймворки и чужие библиотеки, что делает вам x10 буст по сравнению с написанием хранимок.

                  А также: в sql огромные проблемы с реюзом кода. Отсюда вытекают нарушения DRY.

                  У меня к вам вопрос: вы вообще пытались на практике активно писать годик командой в таком стиле?

          • QuickJoey
            /#10669384

            Вы, кажется, не совсем понимаете, что такое база данных. Это далеко не только хранилище, это ещё и хранимые процедуры, функции, триггеры. Которые заменяют backend написанный на PHP, и которым до данных гораздо ближе.
            И вместо того, чтобы дёргать из десктопа за ручки API, написанного на PHP, в котором всё равно написаны SQL команды, я буду дёргать за те же ручки, написанные сразу на SQL.
            Для добавления книги позовём процедуру добавления, что-то типа

            EXECUTE Books_add @AuthorID=823, @BookTitle='Улитка на склоне';
            

            А всё, что происходит помимо собственно добавления книги, например поле «книг всего» у автора, напишу в триггере для таблицы Books
            CREATE TRIGGER tr_Books_insert
               ON  Books
               AFTER INSERT
            AS 
            BEGIN
            SET NOCOUNT ON;
            
            UPDATE Authors SET
            BooksTotal = COALESCE(BooksTotal,0) + (SELECT COUNT(a.AuthorID) FROM inserted a WHERE a.AuthorID = Authors.AuthorID)
            WHERE Authors.AuthorID IN (SELECT a.AuthorID FROM inserted a)
            

            • VolCh
              /#10669434

              И вы считаете это простым поддерживаемым кодом?

              • QuickJoey
                /#10669466

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

                • VolCh
                  /#10669532

                  Мне кажется, что даже с разрешением прямого подключения клиента архитектура системы в целом была бы проще, если бы клиенты тупо исполняли стандартный SQL: SELECT, INSERT, UPDATE, DELETE запросы. Или иметь прослойку в виде апп-сервера, которая вызовы API транслировала бы в такие запросы.


                  Как по мне, то в процедурном SQL слишком много сложностей, обусловленных реляционным характером основных его данных. Вот даже в таком простом примере у вас кроме запроса UPDATE в триггере два внутренних запроса, агрегирующая функция и условие проверки наличия в множестве. Бог с ним, как СУБД с этим разбираться будет, но бедные люди...

            • Hackerook
              /#10669534

              И вместо того, чтобы дёргать из десктопа за ручки API, написанного на PHP, в котором всё равно написаны SQL команды


              Вы мне кажется очень мало сталкивались с разработкой. Если не говорить о устаревших решениях, то люди используют такие вещи как фреймворки, и чаще всего это open source фреймворк, за которым стоит большая аудитория которая регулярно фиксит в нём дыры и т.д и т.п.
              Так вот, в этих фреймворках есть заготовленные базовые модели, которые имеют кучу плюшек вроде методов которые вызываются до исполнения SQL, после исполнения SQL. И к примеру на Ruby on Rails ваш чудесный код можно заменить нижеследующим:
              Добавление книги
              Book.create([{title: 'Книга 1'}, {title: 'Книга 2'}, {title: 'Книга3'}])
              

              Установка в поле «книг всего» значения (P.S делать так это бред, количество книг должно считаться всегда вызывая метод подсчёта в нужный момент, ну конечно если у вас в таблице не гуглплекс книг и она будет виснуть каждый раз при подобном обращении, тогда да, целесообразно возможно)
              after_update :authors_books_inc
              ...
              def authors_books_inc
                 self.author.books_total = Book.where(author_id: self.author_id).count
              end
              


              И код подобный тому что у вас там написан сам сгенерируется и выполнится, ибо подобные задачи обыденные и разработчики фреймворков давно зашили всё это внутрь, чтоб не пришлось писать много кода постоянно.

              Я уже выше писал, но вы попробуйте что нибудь посложнее сделать, чем просто посчитать все книги, количество кода увеличиться в 10-ки раз если делать это на SQL

              • PashaNedved
                /#10669660

                Вопрос в том, а есть ли необходимость в ORM, фреймворках и ООП?

                • nsinreal
                  /#10669976

                  Как человек смотревший на оба подхода в плане long-term я заявляю, что есть необходимость.

                  • PashaNedved
                    /#10670012

                    Интересненько! Задача еще не появилась, а необходимость в инструментах уже есть.

                    • nsinreal
                      /#10670034

                      Да, верно. Если не делать ничего, то и инструменты не понадобятся :-)

                    • Hackerook
                      /#10670404

                      Это тоже самое что отрывать себе ноги и руки перед сном.
                      Мол, ты же ничего ими делать не собираешься ночью, зачем тебе эти инструменты.
                      С утра обратно пришьёшь.

                      • PashaNedved
                        /#10670864

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

                • VolCh
                  /#10670552

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


                  ORM следствие того, что ООП более удобно для моделирования, а реляционные базы наиболее удобны для хранения данных моделирования широкого круга задач.


                  Фреймворки же лишь готовые решения для инфрастуктурных задач, которые обычно не входят в стандартные библиотеки ЯП.

              • QuickJoey
                /#10670580

                Вы кажется не совсем понимаете что такое клиентская часть

                Вы мне кажется очень мало сталкивались с разработкой.

                Вы могли бы, например, перестать оценивать что и насколько я понимаю, и с чем и в каких объёмах сталкивался, а? Спасибо.
                Установка в поле «книг всего» значения (P.S делать так это бред, количество книг должно считаться всегда вызывая метод подсчёта в нужный момент...

                Вот прям бред? Это потому, что вы не уверены в данных (вдруг их кто-то изменил), и хотите посчитать их динамически. А триггер, как раз эту уверенность даёт. Выражаясь в вашем стиле: это бред, считать динамически каждый раз, заведомо знаю, что ничего в таблице не поменялось.
                И код подобный тому что у вас там написан сам сгенерируется и выполнится, ибо подобные задачи обыденные и разработчики фреймворков давно зашили всё это внутрь, чтоб не пришлось писать много кода постоянно.

                Я, извините, категрически не верю, что фреймворк сгенерирует код триггера на изменение таблицы. Думаю там всё гораздо проще.

                … но вы попробуйте что нибудь посложнее сделать, чем просто посчитать все книги, количество кода увеличиться в 10-ки раз если делать это на SQL

                От того, что вы не видите SQL, не значит, что его нет, в итоге у вас и код фреймворка, и SQL, в котором вы разбираться не хотите, надеясь, что за вас напишут хорошо, красиво и быстро.
                Написать посложнее я пробовал, получилось.В одной из сейчас работающих баз данных, 1400 хранимых процедур, большая часть, достаточно простая (CRUD), часть отчёты, часть совсем сложная (финансы, например), но оно работает и самодостаточно, приложение можно написать новое, а логика останется, хоть из командной строки работайте. Плюс на сервере сотня (100) linked server (в основном PostgreSQL), с которыми происходит постоянный обмен данными в обоих направлениях, и всё это (включая сотню постгресов) поддерживается мной. Времени именно на поддержку уходит мало, 1-2%. Я новое разрабатываю, а это живёт само.

                • VolCh
                  /#10670636

                  А триггер, как раз эту уверенность даёт.

                  Не даёт. Что мешает изменить значение поля без добавления записи? Что гарантирует, что кроме триггера на вставку вы написали триггеры на обновление и удаление? Суть принципа "единственного источника правды" и, кстати, вытекающих из него нормальных форм реляционных БД, что есть один и только один источник каких-то данных, при этом какая-то рассинхронизация невозможна, потому что нечего синхронизировать. Денормализация — усложнение архитектуры.

            • michael_vostrikov
              /#10669680

              С ActiveRecord будет вот так примерно:


              $someDbComponent->beginTransaction();
              
              $book = new Book();
              $book->AuthorID = 823;
              $book->BookTitle = 'Улитка на склоне';
              $book->save();
              
              $author = $book->author;
              $author->BooksTotal = (int)$author->books()->count();
              
              $someDbComponent->commit();

              И это только изменение данных, для чего база и предназначена. А еще есть валидация, проверка прав доступа, и отображение результатов этого всего на языке пользователя.

            • areht
              /#10670426

              > А всё, что происходит помимо собственно добавления книги, например поле «книг всего» у автора, напишу в триггере для таблицы Books

              А как выглядит код по уведомлению меня в Телеграмме, что добавилась запись?

              • QuickJoey
                /#10670514

                Не вдаваясь в подробности, где именно это происходит (потому что дёргать url из триггера, это не лучшая практика).
                Выглядит примерно так:

                DECLARE @chat_id nvarchar(50)
                DECLARE @text nvarchar(50)
                DECLARE @url nvarchar(max)
                
                SET @chat_id='12345678'
                SELECT @text='Добавилась новая книга'
                SELECT @url='https://api.telegram.org/bot0987654321:KJHASKkjhuq812n89GxUfg/sendMessage?chat_id='+@chat_id+'&text='+REPLACE(@text,' ','+')
                	
                EXECUTE spHTTPCall @url=@url
                

                • VolCh
                  /#10670558

                  Если не ошибаюсь, то этот код будет работать исключительно под конкретной СУБД. MS SQL Server?

                  • QuickJoey
                    /#10670592

                    Вы правы, но если честно, даже скрипт создания таблицы из двух полей из mssql не переносится в, например, postgres. Или из postgres в mysql.

                    • VolCh
                      /#10670666

                      Именно поэтому создаются всякие DBAL. По сути, если вы пишите SQL код руками не в DBAL, то вы сильно усложняете перенос приложения на другой движок.

                • areht
                  /#10671396

                  > хранимые процедуры, функции, триггеры. Которые заменяют backend написанный на PHP,

                  > потому что дёргать url из триггера, это не лучшая практика

                  А лучшая практика — в бекенде?

                  • VolCh
                    /#10671508

                    На SQL если писать такое, то лучшая практика помещение в триггере задачи на дерганье урл в какую-то очередь (таблицу, исполняющую роль очереди), а там уже по расписанию или ещё как дергать хранимку-обработчик очереди.

    • Naglec
      /#10667108 / +1

      Бизнес-логику на стороне приложения на порядок проще поддерживать и тестировать, ибо там для этого намного больше инструментов.

      • VolCh
        /#10667140

        Именно. Инструментарий для работы с традиционными приложениями развит намного сильнее, чем для СУБД. И многие языки заметно богаче *SQL, особенно если ограничиваться каким-то общим подмножеством для обеспечения возможности относительно простого переезда с одной СУБД на другую.

  10. Zanak
    /#10666462 / +1

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

  11. speshuric
    /#10667342 / +1

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


    "никто не использует просто голую базу данных и сразу вывод на клиент" Оп. Ну-ка назовите хотя бы один банк из TOP-30 России, где нет mission-critical приложения-двузвенки? Я подозреваю, что таких нет (доказать не могу). На самом деле масса критичных тяжёлых legacy систем всё еще двузвенные.


    Самые сильные аргументы против двузвенки (вне зависимости от того, где и как реализована бизнес-логика) на мой взгляд:


    • Каждому клиенту нужно стабильное быстрое соединение чтобы просто работать. Это сразу ограничивает и географию решения, и количество клиентов и любое масштабирование. Промежуточные серверы приложений могут быть и кэшем/буфером и диспетчером пула соединений.
    • При двузвенке сервер СУБД доступен клиенту. То есть прямо в архитектуре заложен RCE. Каждый клиент по определению запускает почти что произвольный код на сервере СУБД. Это ад для DBA. Это боль для ИБ. Это очень сложная управляемость правами, аудитом, ресурсами. Это полная незащищенность от случайного DoS. Никакой resource governor вас не спасет.
    • Нужно тратить ресурсы на обслуживание клиентов: они должны быть гарантированно одной версии, в них приходится закладывать (в лучшем случае косвенно) управление транзакциями и даже хуже — консистентностью распределенных действий.
    • Клиентам тоже непросто. Всё что они незапросили они обязаны держать в памяти даже если оператору это не нужно. На стороне сервера приложений эту нагрузку можно выравнивать.

    Итого: стабильность/устойчивость, масштабируемость, безопасность отсутствуют. Не в абстрактных примерах плохого кода, а в том, что как ты не делай, а оно всё равно для 30-50 клиентов в одной локальной сети и нестабильно.


    Реализация бизнес-логики на SQL это отдельная тема. Этот грех можно легко протащить и в трёхзвенку. Но тут я бы не стал влезать в дискуссию, просто потому что я не могу в отрыве от задачи обсуждать за и против. Можно начать джойнить таблицы на сервере приложений вместо простого запроса и это будет убого. Можно наоборот всё писать на TSQL-PL/SQL и потом удивляться почему для реализации простой фичи нужны гуру СУБД и 6 человекомесяцев. А ведь есть еще разделение между видами задач: OLTP, оперативный репортинг, интеграция, ETL, OLAP — все они хоть немного но пересекаются.
    Лично я против реализации бизнес-логики на SQL, это очень плохой язык вне ниши запросов, но и против догматичного "в SQL только CRUD"

    • RPG18
      /#10667510

      Реализация бизнес-логики на SQL это отдельная тема. Этот грех можно легко протащить и в трёхзвенку.

      А почему это грех? Avito вполне использует.

      • speshuric
        /#10667562 / +1

        О-кей, таки немного влезу в дискуссию :) Ниже моё мнение, а не догма.
        SQL как язык запросов и частично как язык модификации данных весьма неплох. И относительно компактно, и максимально производительно в рамках БД обычно получается. Но как современный язык программирования — он отвратителен. Прямо отвратителен. Модульность — плохо. Ограничение контекста, разграничение видимости на уровне кода — плохо. Переиспользование кода — плохо (или шаг в сторону и потеряли производительность). Модульное тестирование — почти нереально. Очень многие логические, "абстрактные" детали и детали реализации тесно переплетены (индексы, блокировки и вообще). Всё, что ни ткни неидемпотентное и контекстнозависимое. Горы legacy тянущиеся с 80-х годов прошлого века в решениях, в идеях.
        Это не "плач Ярославны". Я с СУБД уже 18 лет деньги зарабатываю, меня сложно удивить нагрузкой, объёмами данных, объёмом кода. Реляционные СУБД (SQL) с нами еще долго и, даже если появится прорывное решение в области обработки и хранения данных, SQL еще не меньше 10-15 лет будет актуален (тупо из-за объёма созданных решений).


        В общем-то я потому и написал "я бы не стал влезать в дискуссию", что понимаю, что команде разработки обычно нужно и решение в разумный срок, и производительность, и масштабируемость, и гибкость дальнейшего развития, а еще нужно, чтобы специалистов можно было нанять (или научить хотя бы). В этих условиях, в поиске оптимального решения, конечно, может оказаться целесообразным и на TSQL написать хранимку на 100к строк (это не шутка). Другой вопрос, что с большой вероятностью этот код придётся выкинуть, потому что его не может понять даже создатель.

        • RPG18
          /#10667814

          чтобы специалистов можно было нанять (или научить хотя бы)

          Не так уж и сложно, по сравнению например с C++.


          Другой вопрос, что с большой вероятностью этот код придётся выкинуть

          Код все равно приходится выбрасывать, потому что бизнес требования постоянно меняются.

          • speshuric
            /#10667858

            Не так уж и сложно, по сравнению например с C++.

            Именно. Потому и пишут на SQL, что там типы — только примитивные (оговорки есть, но несущественные), конструкций мало: select, join и пошёл кодить. И, да, это прекрасно для некоторой категории продуктов.
            Реально в бизнес-приложениях горы кода на TSQL и PL/SQL. Я каждый раз глядя на индексы от TIOBE и подобные усмехаюсь, глядя на код учетных/расчетных и прочих бизнес-систем.


            Код все равно приходится выбрасывать, потому что бизнес требования постоянно меняются.

            100к строк это (оптимистично) 3-5 лет работы только разработчика. Аналитики, тестирование, смежники, сопровождение — плюс к этому. А этот код будет выкинут не потому что он не работает, а потому что никто в нем не может прикрутить небольшую фичу. И затраты на новое решение скорее всего составят не менее 50-70% от стоимости старого. И будет это называться мегапроект "новая система взаиморасчетов".


            То что мне не нравится язык SQL (в текущем его состоянии), то что мне не нравится подход "вся логика в БД" и одновременно то, что этот же язык и подход может быть разумным решением — я в этом противоречия не вижу. Более того и обратная ситуация, когда к базе можно обращаться только CRUD процедурами тоже мне не нравится, и тоже может быть разумным решением.


            Avito в своих базочках использует хранимки? Ну, э… логично же. Запилили версионирование и деплой хранимок? Вообще молодцы. Подходит их опыт для создания единой БД в 10К+ таблиц-сущностей? Мой опыт говорит, что скорее нет, чем да.

            • RPG18
              /#10668312

              конструкций мало: select, join и пошёл кодить

              то что в некоторых базах данных конструкций маловато, это проблемы этих баз данных. Мы не испытываем таких проблем с Postgres. Как видно Avito то же. Если посмотреть видео с конференций по Postgres, то и дело попадаются люди, которые написали логику на хранимках и все работает отлично.


              потому что никто в нем не может прикрутить небольшую фичу

              Badoo переписала часть сервисов с Си на Golang, по этой же причине. Наверное дело не только в языке.

          • nsinreal
            /#10668136 / -1

            Не так уж и сложно, по сравнению например с C++.
            Ну если вы говорите о сложности C++, а не о частоте его используемости, то у вас тут маленький косячок в рассуждениях.

            Дело в том, что людей нанимают не из-за знания чего-то, а чтобы эти люди решали какие-то проблемы. У brainfuck порог вхождения еще ниже чем у sql, но найти senior brainfuck developer будет куда сложнее, даже если язык будет популярен.

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

            • RPG18
              /#10668334

              но не все и не в один момент.

              Если компания занимает ведущее положение в своей отрасли, то она пытается найти новые источники дохода. Поэтому экспериментирует и естественно не всё взлетает. В таких ситуациях приходит CEO, и говорит закруглятся с фичей.


              100к чего?

      • nsinreal
        /#10667754

        Потому что SQL не предназначен для бизнес-логики. Да, в нем есть куча всякого. В теории, вы можете написать небольшой веб-сервер, который будет перегонять все входные данные из запроса в параметры для хранимки и вызывать её. А хранимка будет формировать уже готовый JSON-ответ или html. И ваш тоненький сервер будет отдавать результат клиенту. И можете даже сделать больше одного API (роутинг, и т.д.). Можете. У вас есть средства для этого. Но это не значит, что стоит так пытаться сделать.

        Так вот: я не видел успешных попыток решить задачу через полностью не подходящий инструмент. Это удел хаба ненормальное программирование, а не продакшен кода.

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

        Предложу вам задачку. Напишите мультиплейерный морской бой на чистом SQL. И сравните итоговый код с какой-нибудь реализацией на современном языке программирования.

        • RPG18
          /#10667820

          Где я говорил про html?


          Напишите мультиплейерный морской бой на чистом SQL.

          Где я говорил, про чистый SQL?


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

          то что мы не видели, не означает, что этого нет.

          • nsinreal
            /#10668098

            Да, вы не говорили про html. Html это просто для примера того, что в теории можно сделать на sql

            Где я говорил, про чистый SQL?
            Мм, вы говорили про то, что вы не уверены, что не стоит писать бизнес-логику на SQL. Я предложил вам задачку: написать логику игры в морской бой.

            то что мы не видели, не означает, что этого нет.
            Слыхали про чайник Рассела? Вероятность этого явления чрезвычайно мала.

            • RPG18
              /#10668338

              Мм, вы говорили про то, что вы не уверены, что не стоит писать бизнес-логику на SQL.

              Мы говорили про логику на уровне бд. А хранимки я могу и на JS написать.

        • Chamie
          /#10667862

          Вы, почему-то, под приложением понимаете веб-приложение. Я прав? Для толстого десктопного клиента этот самый «тоненький сервер» и вообще конвертирование в JSON и HTML — не нужны.

          • nsinreal
            /#10668106 / +1

            А что, десктоп приложения еще кому-то нужны в таких количествах, чтобы создавать много рабочих мест?
            В любом случае писать десктопное приложение с прямым доступом к бд — затея так себе. Конечно, за исключением встраиваемых бд (нерасшариваемых).

    • Chamie
      /#10667864

      1. Каждому клиенту нужно стабильное быстрое соединение чтобы просто работать. Это сразу ограничивает и географию решения, и количество клиентов и любое масштабирование. Промежуточные серверы приложений могут быть и кэшем/буфером и диспетчером пула соединений.
      2. При двузвенке сервер СУБД доступен клиенту. То есть прямо в архитектуре заложен RCE. Каждый клиент по определению запускает почти что произвольный код на сервере СУБД. Это ад для DBA. Это боль для ИБ. Это очень сложная управляемость правами, аудитом, ресурсами. Это полная незащищенность от случайного DoS. Никакой resource governor вас не спасет.
      3. Нужно тратить ресурсы на обслуживание клиентов: они должны быть гарантированно одной версии, в них приходится закладывать (в лучшем случае косвенно) управление транзакциями и даже хуже — консистентностью распределенных действий.
      4. Клиентам тоже непросто. Всё что они незапросили они обязаны держать в памяти даже если оператору это не нужно. На стороне сервера приложений эту нагрузку можно выравнивать.
      Вообще говоря, если подходить к архитектуре соответствующим образом, то не так всё будет и страшно. Нужно просто с самого начала рассматривать клиент и сервер как два приложения, взаимодействующие через API. Тогда по пункту 1 клиент может локально кэшировать всё, что можно локально кэшировать, и дёргать только то, что нужно именно синхронизировать с сервером. В пункте 2 дать доступ только к определённому набору хранимок, обеспечивающих API. Никаких произвольных запросов, только вызов отдельных «публичных» хранимок (ну, можно ещё View какие-нибудь).
      3. Достаточно просто поддерживать совместимость/версионирование API, и тогда на версию клиента будет плевать. Про управления транзакциями и консистентностью — ну, так если они в самом приложении нужны, то их так и так придётся делать. Не в одном звене, так в другом.
      4. Зачем им это держать? Поясните, пожалуйста.

      • speshuric
        /#10667954

        > Нужно просто с самого начала рассматривать клиент и сервер как два приложения, взаимодействующие через API.

        Нужно понимать, что со стороны сервера это API не совсем API, а больше похоже на командную строку.

        > Тогда по пункту 1 клиент может локально кэшировать всё, что можно локально кэшировать, и дёргать только то, что нужно именно синхронизировать с сервером.

        Да, только, например в MS SQL подключив одного клиента с тонким каналом (или не успевающего обрабатывать данные) можно во время его работы получить блокировки.

        > В пункте 2 дать доступ только к определённому набору хранимок, обеспечивающих API. Никаких произвольных запросов, только вызов отдельных «публичных» хранимок (ну, можно ещё View какие-нибудь).

        Да, вы мне дали доступ к одной хранимке, а я выполню `begin tran; exec хранимка;` и пойду пить чай не закрывая соединение. А могу в tempdb положить 10e10 записей. Вариантов случайно и специально положить сервер при прямом доступе необычайно много. Пока клиентов меньше полусотни — всё даже ловится и лечится просто.

        > 3. Достаточно просто поддерживать совместимость/версионирование API, и тогда на версию клиента будет плевать. Про управления транзакциями и консистентностью — ну, так если они в самом приложении нужны, то их так и так придётся делать. Не в одном звене, так в другом.

        Да, надо сделать версионирование API, контроль этой версии на клиенте, при версии ниже чем у сервер — обновлять. Это обновление нужно реализовывать. Если код в exe-шнике зашит, то нужно обновить exe-шник (тут нужен механизм проверки контрольных сумм, нужны права определенные). Эту систему придётся обслуживать.
        Про транзакции — см выше пример с `begin tran; exec`. Управление транзакциями с клиента — зло.

        • NoOne
          /#10670542

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