Лишний раз хотелось бы напомнить, что примеры в статье исключительно для понимания сути проблемы. И вовсе не руководство к действию. У них масса изьянов, они не раскрывают лишние аспекты систем, но со своей задачей — донести мысль эффективно справляются. Т.к. максимально упрощены.
В реальности интернет магазин может быть сильно разным. Одно дело торговля 10ю плюшевыми игрушками, а другое дело магазин уровня Wieldberies или Яндекс.Маркет. Есть разница. И если в первом случае гарантированно монолит, то во втором случае микросервисы.
Как реализовывать микросервисы — ну конкретно в этом случае я бы в первую очередь рассмотрел разбиение на основе subdomains (ссылку на то, что это такое и как с этим работать давал в статье по тексту).
И последнее, я тут видео конференций смотрю. И так получилось, что сегодня мне попалось видео, где как раз разбирался такой пример с интренет магазином. Вот ссылка с привязкой ко времени:
Вот эту мысль я не совсем понял. Мы рассматриваем архитектуру системы, косяк в конфиге лоадбалансера — из другой оперы. Что вы здесь хотели сказать?
Попробую другими словами рассказать:
Ну вот вариант 1 с Entity Service для постов и вариант без него. Пердположим при публикации вам нужно выставлять в сущности еще и дату публикации. Это поле должно быть nullable. И по бизнес-логике оно бывает пустым очень редко… в 0,5% случаев. В первом варианте вы добавляете, обновляете создание и обновление. Потом бежите по 4м сервисам и там тоже контракты обновляете (хоть это им не нужно совсем, но они вынуждены, т.к. контракты поменялись). Далее разработчики ошиблись и не сделали в БД это поле nullable. Post service падает в 0.5% случаев. При сохранении черновика публикации — падает. При проверке в двух других случаях — падает, т.к. сохранение не работает. При модерации — падает тоже. Что видит пользователи? Система в целом в этом сценарии не работает.
Рассмотрим вариант два. Вы добавляете новое поле только в сервисе черновиков. И все… все остальные если и получают это новое поле, то игнорируют его. Они даже не менялись. В случае ошибки. Падает сохранение черновиков. Проверка — работает, модерация — работает. Вот что в итоге извне видят пользователи. Вторая система должна им явно больше нравиться и для них она выглядит как более надежная. Упала, но не вся…
Конкретно это — смена публичного контракта сервиса. Такое, будучи неаккуратно сделанным, приводит к проблемам в независимости от того Entity service это или нет.
Это так. Но! у Entitty Service большое, если не сказать огромное число связей. Вот в чем разница. А это значит, если вы меняете в нем контракт, то переписываете большое число фрагментов с бизнес-логикой.
Тоже не совсем удачный пример. Здесь проблема не в том, что это entity service, а в том, что это явный боттлнек в одном инстансе без балансировщика нагрузки и прочего. А если вы хотели донести мысль что «любой entity service с практически 100% вероятностью = боттлнек --> требует доп. телодвижений для обеспечения отказоустойчивости», то это другая мысль и требует отдельного доказательства.
Проблемы с отказоустойчивостью не всегда решаются только лишь наращиванием ресурсов. У вас может быть баг в коде (в каком-то глубоком ифчеке, который не дотестили) до тех пор пока вы не заметили на проде и пока не откатились — у вас страдает N сервисов. А как мы видели из примера особенность Entity Service такова, что он связан почти со всеми. Т.е. страдают все.
И даже в случае с производительностью. Но вот да — у вас проблема, да вы ошиблись с балансировкой и масштабированием и да у вас страдает от этого ПРОД. Одно дело если это какой-то изолированный функционал типа регистрации черновика публикации. А другое дело если это и регистрация черновика публикации. И проверка на плагиат публикации и проверка уникальности иллюстраций и даже сервис модерирования. Т.е. разница в масштабах катастрофы разительная просто…
Это верно вообще в целом если сравнивать монолит и микросервисы. В чем тут отличие конкретно entity service?
С Entity Service мы получаем много, много разных мелких сервисов и какой-то бизнес-сценарий, законченный логический алгоритм начинает ходить по этим сервисам.
Давайте возьмем наш простейший пример с публикациями. Вот если в результируем наборе сервисе мы сделали так: к нам на вход прилетает публикация, мы ее проверили и выстрельнули публикацию + коэф. похожести текста на другие источники в виде числа. Все происходит внутри сервиса. Верно?
А если бы у нас был Entity Service. Мы в сервисе проверки публикации на плагиат получили ИД публикации, слазили в сервис публикаций — вытащили оттуда публикацию, проверили ее, получили коэф. Далее дернули сервис публикации и обновлии поле с этим коэф. Т.е. у нас появились http вызовы в логике проверки. А здесь простейший пример и их всего два. В случае с более реальными примерами будет очень много вызовов и отлаживать такое неудобно… Представьте на секунду что у вас иногда тормозит обновление публикации (при определенных параметрах) у вас 20 таких сервисов. А жалоба к вам поступила вида «Что-то проверка на плагиат медленно работает»
Чтож, в целом вам спасибо за замечания… Подумаем, посмотрим что можно еще дописать. Дополнить :)
Конечно, слепо применять паттерны не нужно. Всегда нужно анализировать каждый случай отдельно, учитывать специфику системы. С другой стороны я не вижу веских доводов использовать в своих системах Entity Service на систематической основе и везде — это ведет к проблемам, что здесь и описаны…
Ну естественно нужно применять инструменты уместно и сразу оченивать обьемы. Вот заказы — 1 в секунду. А наверняка есть еще и товары, настройки, пользователи они меняются раз в день. Может даже реже… Почему бы не сделать ядро системы в одном стиле, остальную часть вынести и т.п. Ну во всяком случае рассмотреть такую возможность.
Потом версионирование ведь тоже по разному можно реализовать. Вот вы упомянули Постргрес. А вот чем Постгрес отличается от SQL Server в плане реализации транзакционности. Транзакции одинаково хорошо и тот и другой реализует. Только у Постгреса происходит работа с разными версиями (внутри движка) а у SQL Server блокировки. И при этом Потгрес не испытывает проблем, есть вакуум и т.п. Паттерн вроде бы один, а реализация может различаться. :)
Согласен! Не нужно везде делать микросервисы. Мы уже выше в комментариях обсуждали — примеры в статье больше для наглядности и простоты. Вовсе не значит, что реальную систему стоит делать именно так…
Сделать сначала монолит, а потом распилить на микросервисы? Я так делал и на самом деле за этим стоит непроработанность требований. Т.е. по факту вы не очень знаете как будет развиваться продукт — поэтому. Накладные расходы на N сервисов при шаблонизации хоть и выше, но не намного.
От системы зависит. Вот у нас конкретный пример. Есть статья. Есть логика про проверки на плагиат, которая выдает числовой коэффициэнт от 0 до 1. Степень плагиата… Ну вот разошлись версии. И что? Запускать процесс сверки? Исправить причину расхождения и отправить еще раз…
В какой-то учетной системе. Где деньги хранятся — да. Согласен. Но ведь ситуация такова, что какой-то простецкий сайт с текстовками начинают проектировать с использованием сверхтяжелых инструментов на serializable уровне транзакций. А это не всегда оправдано… ))
В целом я с вами согласен, что такая проблема есть. Но думаю сильно от системы зависит на какие мы можем пойти исключения.
Вопрос в том где вы прочертите грань. Ну вот предполжим вы отправляете декларацию, которую составили у себя на компьютере в налоговую. А что если та версия в налоговой не будет совпадать с той версией что у вас на компьютере? Вы ведь всегда у себя на компьютере ее поправить можете?
Хранилище то у этих копий разное… Как быть? А не как это нормально. Если вы отправили документ. То вы его отправили именно таким. Именно такой версии. И если вы хотите его обновить — шлете новую версию. И в микросервисах так же. Нужно их просто воспринимать как самостоятельный продукт. И это вовсе не значит, что нельзя будет согласовать взаимодействие.
Все те же налоговые прекрасно работают и принимают документы круглый год. ;)
По моему это проблема любых распределенных систем. Тут только контроль за интерфейсами и схемами данных поможет.
Вопрос в том насколько остро эта проблема стоит в какой-то конкретной системе, в конкретной реализации. В случае с Entity Service, который только CRUD для сущности, эта проблема стоит максимально остро… Потому что почти любой алгоритм с участием такой сущности, любая бизнес логика завязаны на единую точку отказа. И если вы ломаете эту точку отказа — рушится много чего. Все что на нее завязано.
Отказоустойчивость лучше чем у монолита. Так не для этого ли я выделял функционал в отдельный микросервис? В чем тут антипаттерн?
Лучше. Но во-первых можно еще заметно лучше. А во-вторых все это улучшение нивилируется производительностью.
Но ведь Entity Service это только CRUD. Зачем может понадобиться отлаживать CRUD? На практике ведь как получается у вас тормозит выставление счета. Там логика. И что не работает? Берем dotTrace или другой профайлер, смотрим. А тут какой-то из 10 сервисов может тормозить… Какой конкретно? С какими параметрами?
По версионированию могу добавить, что переодически можно делать снепшоты. Это как один из вариантов.
На самом деле мне реализация микросервисов с помощью Entity Service напоминает вывернутый наружу монолит… именно по этому отношу его скорее к антипаттернам и стараюсь при проетировании избегать.
Я думал об этом при написании статьи. Мне показалось, что в случае с интернет магазином получился бы очень сложный пример и пришлось бы придумывать бизнес-логику. Применение пайплайна там само по себе более спорное решение и было бы много вопросов к публикации.
А у меня цель была — саму идею донести максимально сжато без лишних деталей.
Но все таки я подумаю что можно с этим примером сделать. Скорее всего напишу отдельную статью еще одну… но пока думаю как лучше… Так что на связи! :)
Одинаковой версии? Нет, версионирование позволяет вам при изменнии сгенерированть новую версию данных. Предыдущая версия остается неизменной… Но опять же нужно исходить из цели. Предположим вы имеете в виду, что опубликована новая версия статьи. Тогда мы отправляем в шину либо новую версию, либо только измения (если нас беспокоит перфоманс). Остальные сервисы могут как оставлять старые данные нетронутыми, так и удалять их за ненадобностью в зависимости от логики.
Что касается схемы с хранением одной версии данных в одном месте. Тогда мы получаем botleneck. Тут и при изменении схемы могут поехать другие сервисы да и масштабировать сложнее — в целом все это в статье упомянул. Какой из этих вариантов выбрать решать, конечно, вам исходя из той системы, которую делаете. Но я бы все таки как минимум рассмотрел альтернативы.
Тут, всё же, антипаттерн не сам Entity Service, а его неуместное применение.
А есть пример его удачного применения?
В примере с магазином вообще очень плохо вытаскивать на пользовательский уровень почти прямую работу с базой.
В примере подразумевалось либо наличие сервиса-оркестратора (на беке), либо логика живет как отдельные сервисы.
На самом деле подобрать примеры не так уж просто. В них есть недостатки, но цель была другая. Донести общую идею… И с этим примеры вроде как справляются. Опять же если приводить примеры из жизни, то нужно будет погружаться в предметную облать — ОФЗ, Фискальные регистраторы и пр. и т.п. Поэтому предпочел именно эти примеры…
Что ж, позиция понятна. Если нам требуется строгость терминологии — можно назвать и гипотезой. Я вовсе не против, главное чтобы этой гипотезой можно было руководствоваться на практике. За неимением «ничего» — лучше обладать хотя бы гипотезой. Мне подобного рода рассуждения не попадались, если у вас есть чем поделиться — думаю всем было бы полезно.
Нет, я не просто верю Фаулеру. Эти соображения полезны на практике. И в данном случае мой опыт совпадает с опытом Фаулера — на практике все подтвердилось. Это раз. Второе — на самом деле все можно формализовать и реально подсчитать сложность и число действий. Сложность бизнес-логики сводится к числу доменных сущностей, числу связей между этими сущностямии сложности алгоритмов, необходимых для реализации логики. Теоретически такой инструмент сделать можно. Но на практике его нет. Писать его в одиночку? Ну это год или больше, я даже не берусь оценить…
Поэтому и остается лишь одно — делиться знаниеми и идеями только так.
Функциональное программирование — да, формально не рассмотрено. Это опять же тема на будущее… Но опять же, при написании кода с помощью ООП языков сейчас, как правило, используют элементы и идеи функционального подхода. Стараются делать side effect free функции, моанада maybe, цепочки вызовов, делегаты и т.п. Т.е. формально это не чистое ООП.
И хоть у меня на текущий момент не нашлось места для функциональных языков на графике. Мне думается, что мысль, изложенная в статье полезна для анализа кода легаси проектов. И для формирования стратегии по развитии новых сервисов (т.е. как мы будем в случае чего отходить от модуля таблицы к модели предметной области — задаваться таким вопросом имеет смысл)
Например, есть иерархия сущностей: пользователь — базовый класс, оператор — наследник, диспетчер — наследник. Одна из стратегий хранения таких иерархий в реляционных СУБД — это по таблице не класс. Табличка для пользователя, табличка для оператора, табличка для диспетчера. И вот у вас есть некотороая бизнес логика по вычислению «должности с ФИО». Для пример предположим что ФИО в базовом классе, должность в наследнике как поля (только для примера). Вот как эта логика будет выглядеть в коде? Там будет джойн и вот он чисто технический.
Очень много идей в комментарии… на которые хотелось бы ответить :) И я ранее планировал в отдельной статье с примерами на все ответить. :) Если меня не забанят на данно ресурсе, конечно.
Но вот про джойны и условия фильтрации хотелось бы ответись тут. Содержат ли они бизнес-логику? И да и нет.
С одной стороны если у нас есть сущность пользвателя и его адрес. Джой таблиц пользователя и адреса — да, содержит. Простая ассоциация.
Но, если у нас наследование? И мы из базового класса должны вытащить набор полей… тут мы опять же прибегаем к джойну, но это чистые технические детали. Как проверить? Вы говорите человеку далекому от техники (продук оунер, владелец, иногда аналитик) и он воспринимает это как что-то про реализацию, если вообще понимает о чем речь.
Далее давайте просто на синтаксис посмотрим. С одной стороны мы получаем user.Address с другой как минимум две строчки кода from user u… join adress a on u.address_id = a.id И еще про схему не забываем (тоже чистейшай техника). Очевидно, что на любом ООП языке код локаничение и можно проще сосредоточиться на самих правилах, чем на ньюансах хранения.
Все это приводит к мысли — выгрести в оперативку все таблицы, замепить их и условия фильтраци, ассоцииации — все делать в коде. Но даже любой джун вам скажет, что работать такое не будет, вот и получаются хранимые процедуры, методы репозиториев с SQL — по сути одно и то же… И да, они содержат там бизнес-логику. И да, за это приходится платить…
Хм… Такое ощущение, что вы говорите больше про структурное программирование. А это не равно процедурному. Чтож, в любом случае нужно максимально разделить бизнес-правила от логики хранения. Потому что бизнес-правила и безнес-логика и без того очень сложные и если их нагружать техническими деталями хранения (джойны, конвертации типов, условия фильтрации и обобщенные табличные выражения), то читать такой код сложнее. ООП в любом случае борется с такой сложностью очень хорошо… тут уж вне зависимости какой смысл вы вкладываете в понятие процедурного подходо. В идеале вы пишете логику на чистом C#, Java любом другом ООП языке без каких-то лишних вещей. И сериализуете это все в json. Вот в таком случае логика будет в чистом виде, без технических деталей… Но. Производительность. Поэтому и идут на компромисы.
Хотелось изложить свои мысли максимально локанично. Я даже пример кода не стал внедрять, а сослался на код из книги Мартина Фаулера. И да, рекомендовал бы всем освежить в памяти главу 2, прежде чем читать… ИМХО так удобнее. :) По поводу обьектно-ориентированной парадигмы, думаю, все в курсе. Банально — его придумали для того чтобы бороться со сложностью кода более эффективно, чем это делает процедурный подход. Ну вот вспомните был С, появился С++. С какой целью? Сложность кода увеличивалась…
По поводу смешения процедурного и ООП на проекте. Да, это постоянно происходит. Мне просто хотелось бы рассказать про мое понимание влияния соотношения. Т.е. у вас бизнес-логика более процедурная, либо более ООПшная… со всеми вытикающими. И опять же это прямо очень хорошо видно на примере того кода из книги Фаулера.
В реальности интернет магазин может быть сильно разным. Одно дело торговля 10ю плюшевыми игрушками, а другое дело магазин уровня Wieldberies или Яндекс.Маркет. Есть разница. И если в первом случае гарантированно монолит, то во втором случае микросервисы.
Как реализовывать микросервисы — ну конкретно в этом случае я бы в первую очередь рассмотрел разбиение на основе subdomains (ссылку на то, что это такое и как с этим работать давал в статье по тексту).
И последнее, я тут видео конференций смотрю. И так получилось, что сегодня мне попалось видео, где как раз разбирался такой пример с интренет магазином. Вот ссылка с привязкой ко времени:
Попробую другими словами рассказать:
Ну вот вариант 1 с Entity Service для постов и вариант без него. Пердположим при публикации вам нужно выставлять в сущности еще и дату публикации. Это поле должно быть nullable. И по бизнес-логике оно бывает пустым очень редко… в 0,5% случаев. В первом варианте вы добавляете, обновляете создание и обновление. Потом бежите по 4м сервисам и там тоже контракты обновляете (хоть это им не нужно совсем, но они вынуждены, т.к. контракты поменялись). Далее разработчики ошиблись и не сделали в БД это поле nullable. Post service падает в 0.5% случаев. При сохранении черновика публикации — падает. При проверке в двух других случаях — падает, т.к. сохранение не работает. При модерации — падает тоже. Что видит пользователи? Система в целом в этом сценарии не работает.
Рассмотрим вариант два. Вы добавляете новое поле только в сервисе черновиков. И все… все остальные если и получают это новое поле, то игнорируют его. Они даже не менялись. В случае ошибки. Падает сохранение черновиков. Проверка — работает, модерация — работает. Вот что в итоге извне видят пользователи. Вторая система должна им явно больше нравиться и для них она выглядит как более надежная. Упала, но не вся…
Это так. Но! у Entitty Service большое, если не сказать огромное число связей. Вот в чем разница. А это значит, если вы меняете в нем контракт, то переписываете большое число фрагментов с бизнес-логикой.
Проблемы с отказоустойчивостью не всегда решаются только лишь наращиванием ресурсов. У вас может быть баг в коде (в каком-то глубоком ифчеке, который не дотестили) до тех пор пока вы не заметили на проде и пока не откатились — у вас страдает N сервисов. А как мы видели из примера особенность Entity Service такова, что он связан почти со всеми. Т.е. страдают все.
И даже в случае с производительностью. Но вот да — у вас проблема, да вы ошиблись с балансировкой и масштабированием и да у вас страдает от этого ПРОД. Одно дело если это какой-то изолированный функционал типа регистрации черновика публикации. А другое дело если это и регистрация черновика публикации. И проверка на плагиат публикации и проверка уникальности иллюстраций и даже сервис модерирования. Т.е. разница в масштабах катастрофы разительная просто…
С Entity Service мы получаем много, много разных мелких сервисов и какой-то бизнес-сценарий, законченный логический алгоритм начинает ходить по этим сервисам.
Давайте возьмем наш простейший пример с публикациями. Вот если в результируем наборе сервисе мы сделали так: к нам на вход прилетает публикация, мы ее проверили и выстрельнули публикацию + коэф. похожести текста на другие источники в виде числа. Все происходит внутри сервиса. Верно?
А если бы у нас был Entity Service. Мы в сервисе проверки публикации на плагиат получили ИД публикации, слазили в сервис публикаций — вытащили оттуда публикацию, проверили ее, получили коэф. Далее дернули сервис публикации и обновлии поле с этим коэф. Т.е. у нас появились http вызовы в логике проверки. А здесь простейший пример и их всего два. В случае с более реальными примерами будет очень много вызовов и отлаживать такое неудобно… Представьте на секунду что у вас иногда тормозит обновление публикации (при определенных параметрах) у вас 20 таких сервисов. А жалоба к вам поступила вида «Что-то проверка на плагиат медленно работает»
Чтож, в целом вам спасибо за замечания… Подумаем, посмотрим что можно еще дописать. Дополнить :)
Потом версионирование ведь тоже по разному можно реализовать. Вот вы упомянули Постргрес. А вот чем Постгрес отличается от SQL Server в плане реализации транзакционности. Транзакции одинаково хорошо и тот и другой реализует. Только у Постгреса происходит работа с разными версиями (внутри движка) а у SQL Server блокировки. И при этом Потгрес не испытывает проблем, есть вакуум и т.п. Паттерн вроде бы один, а реализация может различаться. :)
Сделать сначала монолит, а потом распилить на микросервисы? Я так делал и на самом деле за этим стоит непроработанность требований. Т.е. по факту вы не очень знаете как будет развиваться продукт — поэтому. Накладные расходы на N сервисов при шаблонизации хоть и выше, но не намного.
В какой-то учетной системе. Где деньги хранятся — да. Согласен. Но ведь ситуация такова, что какой-то простецкий сайт с текстовками начинают проектировать с использованием сверхтяжелых инструментов на serializable уровне транзакций. А это не всегда оправдано… ))
В целом я с вами согласен, что такая проблема есть. Но думаю сильно от системы зависит на какие мы можем пойти исключения.
Хранилище то у этих копий разное… Как быть? А не как это нормально. Если вы отправили документ. То вы его отправили именно таким. Именно такой версии. И если вы хотите его обновить — шлете новую версию. И в микросервисах так же. Нужно их просто воспринимать как самостоятельный продукт. И это вовсе не значит, что нельзя будет согласовать взаимодействие.
Все те же налоговые прекрасно работают и принимают документы круглый год. ;)
Вопрос в том насколько остро эта проблема стоит в какой-то конкретной системе, в конкретной реализации. В случае с Entity Service, который только CRUD для сущности, эта проблема стоит максимально остро… Потому что почти любой алгоритм с участием такой сущности, любая бизнес логика завязаны на единую точку отказа. И если вы ломаете эту точку отказа — рушится много чего. Все что на нее завязано.
Лучше. Но во-первых можно еще заметно лучше. А во-вторых все это улучшение нивилируется производительностью.
Но ведь Entity Service это только CRUD. Зачем может понадобиться отлаживать CRUD? На практике ведь как получается у вас тормозит выставление счета. Там логика. И что не работает? Берем dotTrace или другой профайлер, смотрим. А тут какой-то из 10 сервисов может тормозить… Какой конкретно? С какими параметрами?
По версионированию могу добавить, что переодически можно делать снепшоты. Это как один из вариантов.
На самом деле мне реализация микросервисов с помощью Entity Service напоминает вывернутый наружу монолит… именно по этому отношу его скорее к антипаттернам и стараюсь при проетировании избегать.
А у меня цель была — саму идею донести максимально сжато без лишних деталей.
Но все таки я подумаю что можно с этим примером сделать. Скорее всего напишу отдельную статью еще одну… но пока думаю как лучше… Так что на связи! :)
Что касается схемы с хранением одной версии данных в одном месте. Тогда мы получаем botleneck. Тут и при изменении схемы могут поехать другие сервисы да и масштабировать сложнее — в целом все это в статье упомянул. Какой из этих вариантов выбрать решать, конечно, вам исходя из той системы, которую делаете. Но я бы все таки как минимум рассмотрел альтернативы.
А есть пример его удачного применения?
В примере подразумевалось либо наличие сервиса-оркестратора (на беке), либо логика живет как отдельные сервисы.
На самом деле подобрать примеры не так уж просто. В них есть недостатки, но цель была другая. Донести общую идею… И с этим примеры вроде как справляются. Опять же если приводить примеры из жизни, то нужно будет погружаться в предметную облать — ОФЗ, Фискальные регистраторы и пр. и т.п. Поэтому предпочел именно эти примеры…
Поэтому и остается лишь одно — делиться знаниеми и идеями только так.
Функциональное программирование — да, формально не рассмотрено. Это опять же тема на будущее… Но опять же, при написании кода с помощью ООП языков сейчас, как правило, используют элементы и идеи функционального подхода. Стараются делать side effect free функции, моанада maybe, цепочки вызовов, делегаты и т.п. Т.е. формально это не чистое ООП.
И хоть у меня на текущий момент не нашлось места для функциональных языков на графике. Мне думается, что мысль, изложенная в статье полезна для анализа кода легаси проектов. И для формирования стратегии по развитии новых сервисов (т.е. как мы будем в случае чего отходить от модуля таблицы к модели предметной области — задаваться таким вопросом имеет смысл)
Но вот про джойны и условия фильтрации хотелось бы ответись тут. Содержат ли они бизнес-логику? И да и нет.
С одной стороны если у нас есть сущность пользвателя и его адрес. Джой таблиц пользователя и адреса — да, содержит. Простая ассоциация.
Но, если у нас наследование? И мы из базового класса должны вытащить набор полей… тут мы опять же прибегаем к джойну, но это чистые технические детали. Как проверить? Вы говорите человеку далекому от техники (продук оунер, владелец, иногда аналитик) и он воспринимает это как что-то про реализацию, если вообще понимает о чем речь.
Далее давайте просто на синтаксис посмотрим. С одной стороны мы получаем user.Address с другой как минимум две строчки кода from user u… join adress a on u.address_id = a.id И еще про схему не забываем (тоже чистейшай техника). Очевидно, что на любом ООП языке код локаничение и можно проще сосредоточиться на самих правилах, чем на ньюансах хранения.
Все это приводит к мысли — выгрести в оперативку все таблицы, замепить их и условия фильтраци, ассоцииации — все делать в коде. Но даже любой джун вам скажет, что работать такое не будет, вот и получаются хранимые процедуры, методы репозиториев с SQL — по сути одно и то же… И да, они содержат там бизнес-логику. И да, за это приходится платить…
По поводу смешения процедурного и ООП на проекте. Да, это постоянно происходит. Мне просто хотелось бы рассказать про мое понимание влияния соотношения. Т.е. у вас бизнес-логика более процедурная, либо более ООПшная… со всеми вытикающими. И опять же это прямо очень хорошо видно на примере того кода из книги Фаулера.