Как стать автором
Обновить

Комментарии 144

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

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

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


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

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


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

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


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

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


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

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


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


  1. Очень бедный инструментарий, где на каждый чих вам надо будет придумывать свой инструмент. Деплой, обновление, различные проверки и тесты и так далее.
  2. Большинство разработчиков довольно плохо знают базу данных на таком уровне, что бы писать качественный код, так как опять же, первый пункт привел к большему абстрагированию от баз данных, что привело к тому, что разработчики более детально знают язык, а не базы данных.
0. Отсутствие контроля версий для кода в хранимых процедурах
Расскажите это ребятам из Zalando.
Справедливости ради, вы не совсем правы. Никто не мешает вам хранить исходный код процедур в файлах скриптов и уже делать им версионирование через git, так же как вы делаете это для других языков программирования.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Там ключевое — "заранее планируете и заказываете". А кто там что после этого говорит — дело пятнадцатое.
Вот вам 100 денег — планируйте, заказывайте. Коробка с допилом стоит 150 денег, частично с исходниками 250, разработка с нуля под вас — 300.
То есть вы хотите, что бы за 100 было как за 250? А губозакатывательную машинку не хотите?

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

Двузвенка с логикой на хранимках может быть как следствием "только на этой лекции был", так и "анализ функциональных и нефункциональных требований (см. Акт утверждения требований от ..., далее Требования), включая требования к производительности и масштабируемости (раздел 3 Требований), равно как к поддерживаемости кода во время заданного срока эксплуатации (раздел 4 Требований), показал (см. Приложение 1), что реализация системы на базе двухзвенной архитектуры "клиент"-"сервер(СУБД)" (вариант 1 в Приложении 1) требует минимальных инвестиций для реализации, минимальных затрат на эксплуатацию и развития в рамках Требований утвержденных требований и текущей ситуацией на рынке (см. Приложение 2) и, как следствие, обеспечивает наименьший срок возврата инвестиций (см. Приложение 1). Данная архитектура рекомендуется к реализации в рамках Требований как обеспечивающая оптимальное сочетание основных бизнес-показателей (см. Приложение 4) согласно методике технико-экономического обоснования архитектурных решений, утвержденной Актом №3 от… Однако, обращаем особое внимание, что в случае значительного изменения функциональных требований и(или) даже незначительного выхода рамки нефункциональных дальнейшие инвестиции в развитие системы на базе рекомендуемой архитектуры будут нецелесообразны по сравнению с классической трёхзвенной архитектурой (вариант 2 в Приложении 1) или четырёхзвенной CQRS (вариант 3 в Приложении 1)."

> Двузвенка с логикой на хранимках может быть как следствием «только на этой лекции был», так и… текущей ситуацией на рынке

То есть на рынке много кто «только на этой лекции был»? Не сомневаюсь.

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

> к поддерживаемости кода во время заданного срока эксплуатации

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

Я пытался показать, что "хранимки" могут использоваться как по причинам субъективным типа невладения другими способами (или их нерационального неприятия) без всякого обоснования, так и по вполне логически обоснованным в рамках имеющихся требований.


Интересно что же там такого написано, что нужны хранимки?

Например, "система должна позволять легко корректировать бизнес-логику представителями бизнеса, владеющими SQL". Собственно SQL для этого и создавался.

в рамках имеющихся требований.

Так в чём логическое обоснование то? Что требования минут писать люди, которые были на одной лекции я понимаю.


Например, "система должна позволять легко корректировать бизнес-логику представителями бизнеса, владеющими SQL".

Ну, то есть у бизнеса есть представитель, который был на одной лекции?


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


Во-вторых, с чего вы вообще взяли, что SQL создавался для редактирования бизнес-логики?

Навскидку, объективные плюсы решений "на хранимках" по сравнению с классической трехзвенкой:


  • простота топологии и бОльшая физическая надежность
  • меньшие (в общем случае) объёмы данных, передаваемых по сети
  • отсутствие необходимости лишних преобразований данных
  • бОльшая (в общем случае) производительность

Ну, то есть у бизнеса есть представитель, который был на одной лекции?

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


Ну, во-первых, это бред, никакого «легко менять» там не будет

Это мы знаем, не бизнес.


с чего вы вообще взяли, что SQL создавался для редактирования бизнес-логики?

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

> простота топологии и бОльшая физическая надежность
> меньшие (в общем случае) объёмы данных, передаваемых по сети

Не хотите передавать данные по сети — положите логику на сервере с БД. При чем тут хранимки?

> отсутствие необходимости лишних преобразований данных

У нас преобразования с потерями что ли?

> бОльшая (в общем случае) производительность

premature optimization

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

Да, но им можно вьюшку дать, а не права на хранимки. Да и тот SQL, который создавался, к современному языку хранимок имеет очень смутное отношение.
Не хотите передавать данные по сети — положите логику на сервере с БД. При чем тут хранимки?

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


У нас преобразования с потерями что ли?

Сама необходимость преобразования — это трата ресурсов и во время выполнения, и во время разработки.


premature optimization

Опыт может подсказывать, что не premature, что должен быть запас по производительности, чтобы не начинать оптимизировать когда система уже лежит.


Да, но им можно вьюшку дать, а не права на хранимки.

Заказчик хочет именно прямого редактирования бизнес-правил его сотрудниками. Ну, где-то в хранимке UPDATE oder_discount SET amount = @amount * 0.01 WHERE oreder_id = @id и заказчик хочет чтобы какой-то финдиректор мог зайти и поменять 0.01 на 0.015

> Ну, считайте что под «по сети» я имел в виду «между процессами», сеть или файловый сокет — не суть.

Тогда даже не знаю про что этот аргумент вообще

> чтобы не начинать оптимизировать когда система уже лежит.

Начинайте оптимизировать хоть когда у вас нагрузка в 1%, но пока это premature optimization

> Заказчик хочет именно прямого редактирования бизнес-правил его сотрудниками. Ну, где-то в хранимке UPDATE oder_discount SET amount = @amount * 0.01 WHERE oreder_id = id и заказчик хочет чтобы какой-то финдиректор мог зайти и поменять 0.01 на 0.015

Если у вас там двузвенка потому, что константы захардкожены — могу только посочувствовать.
Тогда даже не знаю про что этот аргумент вообще

Про (не) необходимость IPC.


Начинайте оптимизировать хоть когда у вас нагрузка в 1%, но пока это premature optimization

Аналогично можно сказать про введение третьего звена (апп-сервера) прямо со старта проекта.


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

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

> Про (не) необходимость IPC.

А мы про необходимость IPC обсуждаем?

> Аналогично можно сказать про введение третьего звена (апп-сервера) прямо со старта проекта.

Не мешки ворочать, конечно… Только что «аналогично»? Третье звено — не оптимизация.

> Одна из причин двузвенки —

Я не знаю какие наркотики у вас распыляют, но единственная внятная часть в тексте:

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

И двузвенка тут не нужна
А мы про необходимость IPC обсуждаем?

Двузвенная архитектура предполагает два типа процессов. Трехзвенная — три. Собственно это основное их отличие. И увеличение количества IPC — это объективный минус трехзвенки.


Третье звено — не оптимизация.

Оптимизация. Иначе зачем его вводить?


И двузвенка тут не нужна

Двузвенка тут один из самых простых способов выполнить бизнес-требование.

> И увеличение количества IPC — это объективный минус трехзвенки

Объективный минус — это когда не надо знать что такое IPC, что бы его заметить.

> Оптимизация. Иначе зачем его вводить?

То есть что такое premature optimization вы вообще не понимаете, понятно.

> Двузвенка тут один из самых простых способов выполнить бизнес-требование.

Это ваше мнение, или факт, который вы можете доказать?

И, кстати, это не бизнес-требование. Это у вас бизнес занимается принятием архитектурных решений. Не своим делом занят, проще говоря.

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

"знать", "заметить" — это субъективные категории.


То есть что такое premature optimization вы вообще не понимаете, понятно.

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


Это ваше мнение, или факт, который вы можете доказать?

Это факт, имеющий место быть при совпадении некоторых условий.


И, кстати, это не бизнес-требование. Это у вас бизнес занимается принятием архитектурных решений. Не своим делом занят, проще говоря.

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

> «знать», «заметить» — это субъективные категории.

не объективный недостаток, а субъективная категория, ок.

> (преимущества трехзвенки перед двухзвенной с вики)

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

> Это факт, имеющий место быть при совпадении некоторых условий.

Если вы на одной лекции были? Ну ок, вам быстрее так. А мне вы что хотите доказать?

> Предложение «давайте сделаем бизнес-логику на SQL и ваши спецы будут её менять» встречается с энтузиазмом, поскольку снижает ресурсоемкость проекта почти в два раза.

Зачем вы мне рассказываете печальные истории как продают хреновые решения под пустые обещания двухкратного снижения чего-то-там?

Ну вот я же начал с «После слов «проведён анализ» можно вообще любую чушь с умным видом писать», а вы всё продолжаете что-то этим аргументировать.
SQL-процедуры это тоже исходники. То есть в случае «О, всё на процедурах. Отлично!» была договоренность о получении программы с исходниками за 100 денег. И если бы разработчики всё делали кодом, то отдали бы код.

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


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

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


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

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

Просто нельзя, а проще можно.

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

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

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


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

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


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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

У меня к вам вопрос: вы вообще пытались на практике активно писать годик командой в таком стиле?
Вы, кажется, не совсем понимаете, что такое база данных. Это далеко не только хранилище, это ещё и хранимые процедуры, функции, триггеры. Которые заменяют 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)

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

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

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


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

И вместо того, чтобы дёргать из десктопа за ручки 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
Вопрос в том, а есть ли необходимость в ORM, фреймворках и ООП?
Как человек смотревший на оба подхода в плане long-term я заявляю, что есть необходимость.
Интересненько! Задача еще не появилась, а необходимость в инструментах уже есть.
Да, верно. Если не делать ничего, то и инструменты не понадобятся :-)
Это тоже самое что отрывать себе ноги и руки перед сном.
Мол, ты же ничего ими делать не собираешься ночью, зачем тебе эти инструменты.
С утра обратно пришьёшь.
Давайте поиграем в аналогии? Думаете плотник берет с собой в постель молоток и гвозди, когда спать ложится?
Инструменты и методология были описаны мною выше (ORM, фреймворки и ООП). Не зная задачи и не будучи Нострадамусом, не существует необходимости в этих инструментах.

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


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


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

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

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

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

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

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

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

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

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

Справедливости ради стоит заметить, что за нас могут написать действительно производительно и обеспечить более удобный API.


Мне по работе приходится сталкиваться с аналитикой, которую выполняли на SQL и на Spark. Так вот код на спарке поддерживать на порядок проще, чем код на SQL.


Причины следующие:


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

Одна и та же витрина данных на SQL со всякими оптимизациями может считаться 45 минут, а на спарке 10.

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

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


$someDbComponent->beginTransaction();

$book = new Book();
$book->AuthorID = 823;
$book->BookTitle = 'Улитка на склоне';
$book->save();

$author = $book->author;
$author->BooksTotal = (int)$author->books()->count();

$someDbComponent->commit();

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

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

А как выглядит код по уведомлению меня в Телеграмме, что добавилась запись?
Не вдаваясь в подробности, где именно это происходит (потому что дёргать 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

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

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

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

Гм, и о чём это говорит?
А если вы пишете Java-код руками, то вы усложняете перенос приложения на .Net

Java и .Net — разные платформы, конкурирующие между собой, но не претендующие на совместимость с одним стандартом. MySQL и MS SQL — разные СУБД, претендующие на совместимость с ANSI SQL. Это один из плюсов выбора SQL-движка в принципе.

MySQL и MS SQL — разные СУБД, претендующие на совместимость с ANSI SQL. Это один из плюсов выбора SQL-движка в принципе.
Ну так и следует тогда придерживаться ANSI SQL, если такая цель стоит.

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

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

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

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

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

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

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

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

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


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


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


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

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


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

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

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

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


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

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

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


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

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

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

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


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

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


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


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

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

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


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

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

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

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

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

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


100к чего?

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

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

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

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

Имеем электронный докуметнооборот российского производства. Двузвенный. Несколько тысяч пользователей, онлайн под 1000. Всё не очень хорошо. Неудобно, медленно, глючно.
Всегда можно привести такой пример про любую архитектуру. И про трёхзвенку тоже.
Нужно понимать, что со стороны сервера это API не совсем API, а больше похоже на командную строку.
Т.е., примерно как http-запросы.
Да, вы мне дали доступ к одной хранимке, а я выполню `begin tran; exec хранимка;` и пойду пить чай не закрывая соединение. А могу в tempdb положить 10e10 записей.
Для этого у вас должны быть права на tempdb и этот самый `begin tranc`.
Вариантов случайно и специально положить сервер при прямом доступе необычайно много.
Прямой доступ ≠ произвольный доступ. Если у вас есть права только на запуск 3 процедур, то больше вы НИЧЕГО сделать не можете. Ни положить данные куда-либо, ни транзакцию начать, ни даже сессионные настройки поменять, ни-че-го.
Да, надо сделать версионирование API, контроль этой версии на клиенте, при версии ниже чем у сервер — обновлять.
Ок, не совсем корректно я назвал, не версионирование, не в этом виде. А в виде обращения к методам API определённой версии. Т.е., поддерживаем старые вызовы API по старым адресам (именам процедур) с версией в имени, новые делаем по новым.
Про транзакции — см выше пример с `begin tran; exec`. Управление транзакциями с клиента — зло.
Эмм, а кто-то говорил, что клиент будет управлять транзакциями на сервере? Причём, в виде вот прямо голых SQL'ных begin transaction? Если клиенту нужна будет транзакция — то она ведь не пользователем задаётся, она заранее известна. Весь этот блок кода, который требует транзакции, будет прописан на сервере в процедуре, которую клиент будет просто вызывать с нужными ему параметрами.

По сути описываемая двухуровневая SQL архитектура является трёхуровневой: клиент, апп сервер в виде хранимок и реляционная база данных.

Т.е., примерно как http-запросы.

Неа. Http — это очень простой протокол: всего лишь с десяток методов, прямой и практически безусловный (не читая заголовков типа If-Modified-Since, If-Match, If-Range и подобных) — отсутствует control flow и ни о какой полноте по Тьюрингу речи нет, максимально stateless. За счет этого обработка самого http-запроса достаточно предсказуемая по ресурсам и очень ограниченная по вариативности. Скорее всего именно простота, ограниченность и отсутствие состояния HTTP позволили ему быть таким масштабируемым и популярным.
А командная строка и сессия SQL: порядка сотни разных видов стейтментов, сложный control flow, циклы и полнота по Тьюрингу, сессия существенно statefull, "ресурсоёмкость" выражений очень плохо предсказывается, возможности очень широкие и способов пострелять себе по ногам предостаточно.


Для этого у вас должны быть права на tempdb и этот самый begin tranc.

Вот это были ваши теоретические измышления или практический опыт проектирования и реализации систем? Потому что в MS SQL Server если у вас есть право соединиться, то есть право и на эти команды, если знаете способ — приведите, пожалуйста команды или ссылки на документацию. В PostgreSQL тоже достаточно много всего выдаётся с соединением.


Прямой доступ ≠ произвольный доступ. Если у вас есть права только на запуск 3 процедур, то больше вы НИЧЕГО сделать не можете. Ни положить данные куда-либо, ни транзакцию начать, ни даже сессионные настройки поменять, ни-че-го.

Как это сделать?


Ок, не совсем корректно я назвал, не версионирование, не в этом виде. А в виде обращения к методам API определённой версии. Т.е., поддерживаем старые вызовы API по старым адресам (именам процедур) с версией в имени, новые делаем по новым.

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


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

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


А если посмотреть на реальные двузвенки, то выяснится что там даже на многие лучшие практики просто болт забили, потому что иначе очень сложно разрабатывать. В трёхзвенке это бы было простительным, потому что нет прямого доступа (кроме эпизодических SQL injection), а по факту вся безопасность и стабильность двузвенок держится на честном слове.

На любой тезис почти всегда можно придумать и пример и контрпример. Я мало разрабатывал проектов с нуля, а вот как раз поддерживать веб проектов пришлось много и разных. С моей колокольни это выглядит так, что каждый должен заниматься своим делом, и сервер БД не исключение. Когда сервер БД (речь не про noSQL) начинает слать письма, собирать JSON/HTML или заниматься чем либо еще, не относящимся к своим обязанностям, по хранению и отдаче данных, обычно ничего хорошего не выходит.
Всегда нужно исходить из конкретных требований проекта!
Ситуации, с которыми имел дело, и в которых создание вью и хранимок оказалось оправданным:


  • большие, или сложные логически, изменения данных я бы реализовывал в хранимках, с транзакциями и всеми делами
  • высунуть данные из внутренней системы во внешний мир, которая написана как десктоп приложение, и гарантированно непозволить веб серверу испортить данные (например в случае взлома вебухи) или надолго заблокировать чувствительные для других таблицы.
  • контролировать права на уровне доступа к хранимкам, а не таблицам, которых может быть заметно больше, и их количество может со временем меняться.
  • последняя линия обороны от неправильных данных, в контексте таблиц и полей, если остальные меры не справились.
Когда был молодым и трава была зеленее, тоже занимался подобными извращениями :) Заставлял MS SQL делать практически все, взаимодействовать с другим ПО, СМСки отправлять, и даже сообщения в ICQ. Но делал это по другому, писал свою DLL в которой реализовывал нужный функционал, и потом из MSSQL её вызывал. Ну в те времена это было круто :)))
Статья начинающего холивращика :)
Вот тут ключевая ошибка
>Нужно использовать данные из другой СУБД — просто делаете запрос в другую БД и спокойно
>работаете с этими данными.

Единая БД — чертовски удобное место для манипуляции данными, и практически единственное, если нужно работать с большими объемами согласованнымх, транзакционных данных.
Представьте, что данные по продажам какого нибудь Дикси лежат в одном экземпляре БД, а данные по скидочным акциям контрагентов — в другом. И как тут спокойно создать, скажем, отчет по ребэйтам — тащить все миллиарды записей в миддлваре и там компоновать?
Стоимость межпроцессных взаимодействий никто не отменял, и если можно затолкать все данные в одну БД — надо заталкивать.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации