Python conf in Kharkiv, Nov 16 with Intel, Elastic engineering leaders. Prices go up 21.10

Засилье анемичной доменной модели

Уважаемые разработчики и прочие посетители ДОУ!

На протяжении лет и лет Java — разработки я заметил одну нездоровую особенность дизайна (технического) проектов в ИТ. Наблюдаю совершенно бесстыдное распространение анемичной доменной модели, когда типичное Java backend приложение пишется как Сервис-Дао-Хибернейт, при этом собственно вся логика производимого действия сокрыта в сервисе. Пр и этом классы собой представляют бины, зачастую разработчики даже делают их неизменяемыми и очень ними гордятся.

Изучая и Фаулера, DDD, и сам участвуя в разработке проектов, я четко убедился в превосходстве объектно-ориентированной модели организации кода, а именно введения слоя доменной модели, и размещения логики в классах доменной модели. При этом слой сервисов естественно остается. но каждый метод сервиса теперь маппится четко на сценарий (Use-case), и содержит вызовы относительно высокоуровневых методов этих самых бизнес-объектов. При этом классы доменной модели изменяемы, в основном, так как представляют собой полноценные классы, а не кастрированные бины. Собственно неизменяемость это хорошо, но это хорошо для дата-классов, которые есть в Скале и Котлине, а в джаве нету. Для обычных классов это не всегда хорошо, так как запутывает разработчика, потому как настоящие классы в ООП служат как раз таки для инкапсуляции изменяемого состояния в основном. А вот для дата-классов, неизменяемость это конечно плюс.

Я намерен бороться по возможности с анемичной доменной моделью.

А вы?

LinkedIn

Лучшие комментарии пропустить

Я намерен бороться по возможности с анемичной доменной моделью.

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

В rich domain model есть несколько недостатков:

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

* Необходимость передавать зависимости в экземпляры объектов. Особенно это весело в сочетании с ORM. Либо добавлять внедрение зависимостей в ORM, либо использовать отдельные модели для маппинга таблиц и для предметной области (это не всегда плохо, но часто излишне)

* Необходимость поднимать объект в память для вызова операции. Для добавления строки в заказ нужно загрузить заказ в память. С одной стороны, после добавления строки в анемичной модели заказ придется перезапросить из базы, а в богатой модели все вычисления уже сделаны в памяти, с другой стороны в анемичной модели будет короткая транзакция, т.к. обновление будет происходить на актуальных данных в базе, а в богатой модели нужно использовать оптимистичные блокировки, иначе легко получить неконсистентность на часто изменяемых объектах. Конечно же, можно и в богатой модели обновить сразу базу, а потом перечитать, но это влечет за собой сразу несколько проблем, в том числе инвалидацию графа лениво загружаемых данных, плюс ОРМ должен уметь перегружать данные в тот же экземпляр объекта (либо, опять же, писать отдельную модель для DAL) . Ну и это опять же будет уже не совсем rich domain model.

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

Ну и нафиг оно нужно?

Я намерен бороться по возможности с анемичной доменной моделью.

Зачем? Какое валуе вы ожидаете получить?

Основным недостатком rich domain model является то что для ее построения надо глубокое понимание «домена» (bounded context). Это усложняет ее использование в «аджайл разработке», поскольку интерфейс домена может изменится в любой момент (в любом спринте :) ).
Другими словами rich domain model требует наличия фаз «оценки требований» и «дизайна», а не просто «х...-х... и в продакшен».

ри этом слой сервисов естественно остается. но каждый метод сервиса теперь маппится четко на сценарий (Use-case)

Все что пишет Дядя Боб надо фильтровать :)

Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

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

Так а что с зависимостями делать?

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

Зависимости — это внешние по отношению к объекту (и аггрегата в т.ч.) сущности. Соответственно зависимости — удел сервисов и аггрегатов. Если мы достаем данные нужные для чего-то извне объекта, то и зависимости должны находиться вне объекта — в сервисе, аггрегате.
Как и методы, которые работают используя эти самые зависимости.
Внешние для аггрегата зависимости для доставания внешних для аггрегата данных — вне аггрегата (в его сервисе, аггрегате аггрегата). Да, получается «капуста», но без крайних состояний как в сторону Бугаенко, так и без крайних ситуаций, когда объект модели данных является просто переносчиком данных в анемичной модели. Но это все про близкие по духу зависимости, которые ограничиваются баундед контекстом.

Это слишком сложно. Ad-hock доступ на чтение/запись позволяет построить простую, понятную архитектуру, которую со временем становится сложно развивать и поддерживать.

Не совсем понял, что имелось ввиду.

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

Есть два варианта: передать в заказ или метод расчета скидок либо репозиторий с заказами, либо коллекцию заказов. Какой из вариантов, по-вашему, правильный?

Мой вариант начался бы с вопроса: «Если расчет скидки подразумевает работу с бОльшим количеством объектов типа „Заказ“, чем один, должен ли этот метод быть внутри заказа?»
Я бы скорее всего его вынес в какой-нибудь РасчетСкидокСервис, который работал бы со списком заказов и рассчитывал бы их для юзеров.
Ведь скидка — это не про заказ. Это про юзера, который сделал несколько заказов. И получает скидку не заказ, а юзер. Или не получает. :)
Вспоминая логику, возможно сделал бы отдельное поле юзеру «Скидка», в котором бы держал пересчитываемую информацию о том, накопилась ли у него уже скидка, если да, то какая.
Ну и конечно Сервис держал бы в себе внешние зависимости про репозиторий и если еще что-то надо.
АПД: Мыслями навеяло. Расчет скидки даже наверное был бы ближе к внутренностям объекта юзера, ведь он предварительно должен в себе содержать всю нужную информацию про историю заказов этого самого юзера. Если нет — то юзер сервис, который будет находить эту самую историю и по ней считать скидки.

Вот! Я бы тоже так сделал.

Но, по идеологии рич модели, сумма заказа — это инвариант агрегата Заказ, и расчет должен проводиться самим агрегатом.

Тут можно вернуться к моему призыву руководствоваться здравым смыслом в рамках понимания рич и анемик моделей, и доменной модели, ведь любая аппликуха является (должна являться) и отображением реального бизнеса, который за ней стоит. Вместо того, чтобы ломать копья какая книжка или теоретическая статья лучше. Сейчас как раз много думаю на эту тему: ведь в реальном мире (по крайней мере в моем кейсе) нельзя быть просто погромиздом, нужно разбираться и доменной области бизнеса для которого пишешь код. Т.е. по хорошему нужно быть техническим экспертом для решения проблем бизнеса, а для этого нужно в этом самом бизнесе разбираться и понимать что в нем важное / главное для клиента, а с чем можно подождать или вообще забить. И этот вопрос для меня сейчас самый сложный, а не попытки понять куда какую сущность запихнуть или где какой метод / функционал должен быть расположен. Ведь когда ты понял бизнес клиента (на своем уровне конечно), вопросы типа «куда запихнуть этот долбанный функционал?» как-то отпадают сами собой.
АПД: Выше я какбы намекаю на то, что: что явлется инвариантом чего или частью, или связанным в один контекст, или не связанным, должен диктовать бизнес (его модель), а не техническая теория. Ведь мы технически пытаемся отобразить бизнес, а не пытаемся натянуть сову на глобус, бездумно следуя технической книжке или набору технических статей. Ну по крайней мере должны пытаться :)
Ну и продолжая на конкретном примере про сумму заказа: А почему она должна быть быть частью именно заказа? Почему бы ей не быть привязанной к Юзеру, т.к. деньги за заказ (эту самую сумму) платит юзер? Учитывая, что для расчета этой суммы мы обязательно еще должны «заглянуть» в скидки, которые тоже часть юзера и определить сколько именно этому юзеру надо заплатить денег в данном конкретном случае.
Или еще вариант родился: Пусть сумма будет частью заказа (допустим нас и так устраивает), а Заказ частью Юзера (как и скидки), тогда для расчета суммы заказа (с учетом возможной скидки) у нас есть все данные на уровне Юзера.

Но, по идеологии рич модели, сумма заказа — это инвариант агрегата Заказ, и расчет должен проводиться самим агрегатом.

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

Это идеологически правильно, но технически неэффективно (без ленивой загрузки, и соответственно, передачи зависимостей внутрь агрегата).

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

Споры ООПшников. Jaba vs gadiuka.

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

Не підходить: хранимки все псують. А чистої реалізації без них... мабуть, вже нема.

А почему должно быть засилие ООП?

AAYYYYYYYY LMAO

Орнул с этой темы. А ничего что DDD это FIRST AND FOREMOST не про код, а про подход к разработке? Самое важное в этой всей теме это ubiquitous language, то есть научить разработчиков, доменных экспертов и бизнес говорить на общем языке, и языком этим может являться та или иная модель.

Fundamentally, DDD is the principle that we should be focusing
on the deep issues of the domain our users are engaged in, that
the best part of our minds should be devoted to understanding
that domain, and collaborating with experts in that domain to
wrestle it into a conceptual form that we can use to build
powerful, flexible software.

- Eric Evans

Rich model этом контексте это всего лишь пример как можно замапить модель на код, только и всего, и причем не подходящий 99% проектов. В большинстве случаев anemic model это то, что нужно.

Don’t try to apply DDD to everything. Draw a context map
and decide on where you will make a push for DDD and where
you will not. And then don’t worry about it outside those
boundaries

 — Eric Evans

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

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

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

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

Откуда программистам знать сленг каких-нибудь финансистов или там insurance agents? Собственно все логично.

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

Очевидно же, что в этой теме обсуждается tactical DDD, а не strategic DDD, потому что с него проще начинать въезжать.

Вот любопытно, приходит человек в тред, одним из первых комментов пишет что и rich, и anemic model может одинаково успешно применяться при моделировании предметной области, используя набор простых правил. Его при этом называют адептом rich model, навешивают еще кучу ярлыков и говорят, что Бугаенко неправ. Что не так с этим сообществом?

Что не так с этим сообществом?

ответ давно содержится в lurkmore.to/ДАртаньян

В деяких компаніях це ДНК бізнес-моделі. Може варто набратися досвіду і перейти в кращу компанію.

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

ІТ всього лиш обслуговую основний бізнес.

Имхо, это уже спорный тезис.

Весь мир плотно сидит на ИТ. Бизнес-модель без своего ИТ-отдела это максимум ларек, шаурма или забегаловка. Изыми из любого крупного бизнеса ит — он мгновенно перестанет быть, раздавленный конкурентами в борьбе за сервис и клиенториентированность. Бизнес-модель и его ит-компонент, гарантирующий охват и доступность нужно рассматривать как неразрывное целое.

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

Имхо, это уже спорный тезис.

не спорный, а так и есть.

Просто пример — а давайте тогда уволим неправильный.
Типа — раз на улице спокойно — так давайте разгоним полицию, зачем она нужна?
Раз 80% покупателей покупают 20% ассортимента — то на кой нам морочится с остальными 80% ассортимента, давайте оставим в магазине только те 20% самых покупаемых
У меня здоровые зубы — так зачем мне утром и вечером чистить зубы?

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

Еще никто не сумел предметно возразить такому аргументу.

просто не попадались осведомленные наверное :)

например о боооольшом таком преувеличении программистами о себе

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

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

Много болтологии ни о чем.

рядовой менеджер обычно да

Любой менеджер. На этот вопрос ни у одного менеджера не будет ответа. Потому что если нас наняли — мы нужны. Раз мы нужны — значит без нас телега не поедет. Если %title_name% хочет возразить — пусть он докажет свою правоту, уволив ИТ.
Что, нет? Не получается? Интернет-магазин не работает без интернет-магазина? Сайт сам собой не разрабатывается, а бизнес без сайта не существует? Все данные в базе данных?

Ах ну да.

Не смог уволить — так иди ка, %title_name%, к себе в кабинет и занимайся своей работой, а рассказывать про ненужность будешь кому-то другому.

Если вы мыслите не так, то это исключительно ваши проблемы, а не ит.

Раз мы нужны — значит без нас телега не поедет.

а без уборщиц в офисе :)

Если %title_name% хочет возразить — пусть он докажет свою правоту, уволив всех уборщиц.

Если вы мыслите не так, то это исключительно ваши проблемы, а не ит.

тот кто распоряжается бюджетами вполне знает место и уборщицам и айти :)

ну а уборщицы и айтишники могут о себе думать что пожелают :)

Сайт сам собой не разрабатывается, а бизнес без сайта не существует?

есть в историях для детей о споре кто важней, левая рука или правая.
для постарше — печень или сердце.

так и ваши аргументы

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

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

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

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

Мутный поток сознания ниочем.
Если вы сравниваете себя с уборщиком это ваша психологическая проблема.

Вы представьте что все права на ваш код вам отдали.

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

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

Если вы сравниваете себя с уборщиком это ваша психологическая проблема.

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

а даром, ваш код то хоть кто-то возьмет, раз он приносит успех?

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

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

А у вас просто психологические проблемы с самооценкой.

Я уже продал свой труд работодателю

как и уборщица.
у нее тоже нет производимого продукта :)

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

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

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

открою секрет
вам разжевали и в рот положили — какой код нужно написать
да, это непростое дело, не каждый сможет.

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

А у вас просто психологические проблемы с самооценкой.

это да, вы кроме того что спец в экономике предприятия так еще и психотерапевт :)
может еще и телепат. тоже частый навык :)

Мне не нужно никому ничего доказывать

и разместили пост где доказываете :)
правда не бизнесу, форум то айтишный :)

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

Типа — раз на улице спокойно — так давайте разгоним полицию, зачем она нужна?

Этот пример и следующие — это примеры или к страховке, или к факторам естественной деградации, которые надо компенсировать работой.
Работа IT отдела тоже укладывается на 99% в эти два фактора, и показывать их надо по аналогиям.
Полиция — не только ловля конкретных преступников, но и защита, чтобы их не было.
Поддержание IT — аналогично поддержанию здания в порядке — где-то посыпется, где-то облупится, перекрашивать надо, и всё такое.
Аргументы в таком стиле понимает любой руководитель. Но типовой руководитель обычно не понимает, почему админу надо думать, а не разгружать товар, пока он ничего конкретно не чинит. Вот это объяснить — самое сложное.
Сейчас как-то это стабилизировалось за счёт рынка IT-услуг. Раньше был вообще бардак.

Еще никто не сумел предметно возразить такому аргументу.

Увы, возражение находится сразу: «OK, вас увольняем, других нанимаем, которые сделают, что нам нужно. У нас реальный бизнес, а вас тут таких много.»
Спорить с этим можно только конструктивно (вот как раз в духе соседней темы про факапы — «Что конкретно вы предлагаете (как представляете себе)?» и на основании каких-то реальных аргументов в духе «вот это будет вложением на месяц, вот это — на год, а если ещё подумать — то на 10 лет — но думать это стоит ещё 3 человеко-месяца».
Ларёк — не согласится, сеть из 100 ларьков — уже вероятно, из 1000 — точно.

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

Учитывая тотальные проблемы с наймом специалистов если вам говорят «вас таких тут много», имхо варианта только 2 — или вы низкоквалифицрованный специалист или вами манипулируют.

«много» только нулевых джунов после курсов. И то нормального джуна днем с огнем не найти.

cast “Invocation S. Nemchinskiy”...

Типичная ситуация:

Есть два класса пользователей одной и той же системы
«админстраторы»
«домашние пользователи»

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

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

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

в особо тяжелых случаях применяют вообще такое
администраторы работают с реляционным представлением данных
а ядро системы перекидывает данные в какую-нить Кафку или Касандру

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

Рич модель толкает к тому что разницы между этими типами транзакций нет
и к тому что нет разницы между «администраторами» и «домашними пользователями»

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

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

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

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

анемичная модель тоже ничего не исключает :)

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

второе обычно наблюдается у энтерпрайзников

P.S.
холивару этому действительно много лет
поэтому оставлю цитату и ссылку на один пост, в котором как-то отписался

Преимущества шаблона Анемичная модель предметной области

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

Несколько слов в защиту шаблона «Анемичная модель предметной области» (Anemic Domain Model) 2012 г.

ну а про истоки фундаментальности проблемы можно нагуглить
«Вьетнам компьютерной науки»

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

у меня вот субподрядчики только на второй неделе начали задумчивые вопросы задавать.
а вначале то — ха, сейчас распилим на микросервисы(рылли? ок, давайте implementation plan и будем пилить. одно ограничение: чтобы двух копий данных не получилось :)
ха, у нас есть расширение для применяемой ORM, которое бац, и relation по умному делает
отлично! применяйте! только... я слабо представляю как вы сделаейте эффективной по скорости отношение к данным которые появляются в результате агрегирования... но ок, давайте!
ха ...

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

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

использования CQRS, который очень эффективно решает
CQRS
очень эффективно решает

)))))))))))))))))))

Это такой тонкий (толщины круглой скобочки) троллинг? :)
С удовольствием читаю твой Телеграм-канал, очень толковые вещи пишешь.

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

Немного спутаны слои приложения.
В нормально спроектированной системе метод в рич модели делает две вещи:
1. Валидирует что возможен переход из состояния А в состояние Б
2. Выполняет этот переход
И это не зависит от того, кто вызывает эту логику — Администратор или Пользователь, потому что всегда и везде эта логика работает для всех. А если не работает — то это уже другой метод, с другой специфической бизнесс логикой.

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

Немного спутаны слои приложения.

слоев приложения не существует :)

их придумывают.

Валидирует что возможен переход из состояния А в состояние Б

кто валидирует?
где расположено это знание?

Выполняет этот переход

а переход может быть асинхронным.
например — требовать разрешения — модератора.

И это не зависит от того, кто вызывает эту логику — Администратор или Пользователь

еще как зависит.

потому что всегда и везде эта логика работает для всех

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

И еще есть тонкий момент с пермишенами

это вообще мелочь :)

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

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

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

До речі це гавно уже доплило до Rails і там тепер все частіше можна бачити відмову від концепції «fat model thin controller» та перехід до процідурок які тепер називаються «service objects».

А так-то в Rails завжди були нормальні моделі і ордер процесив сам себе, а не за допомогою якогось OrdersService.

И при наличии логики чуть больше, чем ничего мы получали дикие по величине модели, callback hell и так далее.
Вы простите, но работать с большими моделями, да еще в которых намешана бизнес логика — это ...

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

дикие по величине модели

Тю, взяли і витягнули логіку в окремий клас. Тіки такий який сам себе процесить а не який процідурками звертається до ордера і ковбасить його.

callback hell

Відома проблема, проте мені якось вдалося її уникати на всіх проектах. Колбеки я використовував тільки для нотифікашок у треті системи, і то, дуже обмежено.

да еще в которых намешана бизнес логика

лол, а що там ще може бути?))) чи ви теж свідок service objects?

Тю, взяли і витягнули логіку в окремий клас.

то есть мы так и так принцовываем к появлению God класса

Тіки такий який сам себе процесить а не який процідурками звертається до ордера і ковбасить його.

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

то есть в модуле Order должно быть как минимум 2 сервиса:

CreateService
UpdateService

и внешний метод там только один — call

лол, а що там ще може бути?)))

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

чи ви теж свідок service objects?

можете хоть горшком назвать, дело Ваше.

Сервис должен выполнять конкретную бизнес операцию.

то есть в модуле Order должно быть как минимум 2 сервиса:

CreateService
UpdateService

и внешний метод там только один — call

Такое действительно написано в какой-то ООП «библии» ?! Да и зачем, по сути, нужен сервис для элементарных атомарных операций? Насколько я помню, сервисы нужны для сценариев, где нужно «дирижирование» операциями над несколькими сущностями / агрегатами, но уж точно не для CRUD в рамках одной сущности.

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

но уж точно не для CRUD в рамках одной сущности.

Очень часто и для одной сущности нужно.
Есть есть к примеру группа после операций, или надо сделать часть работы асихронно и так далее.

то есть мы так и так принцовываем к появлению God класса

топ кек.
хто сказав шо клас має бути один? Якщо один з процідурками — то це й є ті самі service objects.

CreateService
UpdateService

и внешний метод там только один — call

)))))))))

Мое личное мнение — модель не должна содержать в себе бизнес логики.

)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
Ясно, +1 до Богдана, далі можна не продовжувати.

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

))))))))))))))))))))))))))))))))))))))))))))))))

Скільки людям об’єекти не давай, а вони все одно скотять все до паскалю з модулями та процідурками.

Тю, взяли і витягнули логіку в окремий клас. Тіки такий який сам себе процесить

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

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

Зробили один об’єкт зверху, запхали туди транзакцію, далі розкидали по дрібним об’єктам, зробили там те шо стосується кожного, в кінці закоммітили транзакцію, ізі!

то Вы получите очень большую проблему.

Ага )))

ізі

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

Правильно, вы выносите процессинг и получаете все то-же самое как и пинаемый Вами сервис.

Є дуже велика різниця між тим коли об’єкт сам себе процессить по інкапсульованим даним та між тим коли його ззовні ковбасить «сервіс» по геттерам та сеттерам.
Воно ніби одне й те саме але насправді ні.

Короче, почитайте Єгора, якщо ще не читали — www.elegantobjects.org

Є дуже велика різниця між тим коли об’єкт сам себе процессить

Не сводите все к тупому CRUD, пожалуйста.

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

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

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

Короче ти не поняв про шо я говорив. Шо підтверджує мою тезу про те що люди або врубаються в Єгорівщину або зразу або не врубаються взагалі шо ти їм не розкажеш або покажеш.

Треба просто робити проекти, тоді непорозуміння відпадуть самі собою.

Шо підтверджує мою тезу про те що люди або врубаються в Єгорівщину

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

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

Пушо так і є. Тільки не я один, а ще купка ентузіастів. С десятка колег з якими я працював в Єгора в’їхав тільки один тіп.

Люди або приймають це або не приймають і все. Мені норм зайшло з першого поста який я прочитав, ще в 2016. Іншим не заходить ну і ок.

Сидіть далі пишіть сервіси та дтошки і називайте це об’єктами.

вырожденных

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

Блін дядь ну давай ти чесно скажеш шо не дивився сорци єгорівських проектів та й все )))

А я гдето говорил что смотрел сорцы его проектов? Вроде нигде не говорил.
Или ты думаешь что идеи Егора какието вот прям уникальные и больше нигде нету его коллег? Можно даже Фаулера прочитать, он тоже иногда телеги задвигает крайне далекие от жизни, тебе бы зашло кстати :)

Понимаешь, чтобы быть в курсе о чем топят адепты «тру ооп» и вот это все, не нужно сдавать тебе экзамен на знание сорцов Егора назубок :)

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

якщо ще не читали

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

Зробили один об’єкт зверху, запхали туди транзакцію, далі розкидали по дрібним об’єктам, зробили там те шо стосується кожного, в кінці закоммітили транзакцію, ізі!

ммм, спагетти, омномном.

там тепер все частіше можна бачити відмову від концепції «fat model thin controller» та перехід до процідурок які тепер називаються "service objects".

Поздравляю, на рельсах начинают писать код, отличный от крудов :) взрослеют ребята :)

рельсах начинают писать код, отличный от крудов

гітхаб, гітлаб, бейзкемп та шопіфай передають привіт з 2010.

гітлаб

github.com/...​/gitlabhq/tree/master/app

Написано кстати с логикой в сервисах, а не в модели)))))

Кстати, рекоммендую почитать эти три документа Effective Aggregate Design by Vaughn Vernon. Там и про выбор ответсвенности и про граници транзакций и про eventual consistency между агрегатами.

Комменты не читал, но как на счет FP, моделирования предментной области в функциональном стиле?

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

Именно. И говорят, что проще поддерживать и тестировать. IO выводится на самый верхний уроветь и все остальное становится предсказуемо и понятно. Как, собственно, и с грамотно смоделированной Rich Domain Model. Просто в FP по-другому не получится.

Вже писав — dou.ua/forums/topic/26959

Привіт бро, а ти Єгора Бугаєнко катіруєш?

Сорян, але анемічні моделі ти ніколи не побореш. Навіть на цьому форумі 80% «сіньорів» покрутять пальцем у скроні, запишуть тебе в навіжені і підуть далі круди робити.

Я тобі так скажу.

Єгорівські ідеї або відразу людині заходять і після того вже «невозможно развидеть увиденное», або не заходять абсолютно, причому люди, яким вони не заходять, походу перебувають у квадранті неусвідомленнної некомпентеності, тобто вони тупо не врубаються чому те, як вони роблять (дто-контролер-сервіс-репозиторій) це тухла фігня а не норм архітектура та підхід.

І жодні доводи вони будуть відкидати а потім ще й пальцем на тебе показувати «гагага дивись, єгора256 обчитався зараз будуть скобочки ))))))». В принципі в цьому треді ми це і спостерігаємо.

Єгорівські ідеї

Это как раз тот бред, который возникает у большенства неучей, когда они начинают знакомится в нормальнм ООП. И это объясняет почему «rich domain model» не популярна: для бизнеса предсказуемей все делать в процедурном стиле (мой любимый паттерн — транзакшенал скрипт :) ).

Лучше почитайте того же Фаулера или даже Дядю Боба, потом метериалы с OOPSLA и если это осилите, то можете копать в глубь смолтолковых комюнити.

А ось прибув і перший пасажир, про яких я говорив!

причому люди, яким вони не заходять, походу перебувають у квадранті неусвідомленнної некомпентеності, тобто вони тупо не врубаються чому те, як вони роблять (дто-контролер-сервіс-репозиторій) це тухла фігня а не норм архітектура та підхід.
А ось прибув і перший пасажир

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

А тажке нужно сказать, что Егоровские идеи работают исключительно в сефрично-вакуумном идеальном языке самого Егора. Это который без статик, без нулл и т.д.

Егор это такой Да Винчи, который строит идеальные города, в которых никто никогда не будет жить и дивные вертолеты из бумаги, которые никогда не будут построены, потому что хороши только на чертежах. Как и работы Леонардо, основная ценность работ Егора (имхо) — в исследовании пределов гипотетических абстракций, построеных разумом. Возможность физической реализации в реальной жизни Егор предпочитает упускать. Большинство его идей требуют написание новых технологий с нуля.

озможность физической реализации в реальной жизни Егор предпочитает упускать

Ага, саме тому в нього на гітхабі купа проектів написанних в EO-стилі.

Да пускай, это ничего не доказывает, по сути.
Кузьмин вон тоже процессор разрабатывает.

Да пускай, это ничего не доказывает, по сути.

> На EO проектів немає, концепція Єгора не працює
> ...список проектів на ЕО
> це нічого не показує

))))) ок )))))

> це нічого не показує

Именно.
Это будет чтото доказывать, когда это станет массовым. Когда егор-стайл проекты откусят хотябы какойто ощутимый % сферы.
Пока что «У Егора есть проекты в гитхабе» это не более чем proof of concept, что его идеи не совсем абсурдны.
И да, ты прав в том, что я не совсем правильно сказал.

Возможность физической реализации в реальной жизни

следует читать как «соотношение реальной ценности реализации идеи к трудозатратам на ее реализацию и ценность конкретного результата».

Я сейчас не смог найти ссылку, но он как-то хвалился своим написанным в идеальном стиле супер-веб-сервером, а потом так тихо добавил, что в нём никогда не будет поддержки websocket :)

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

Вперше в цій темі почув про існування такого єгора, але не полінився, заглянув в його гітхаб, і, схоже, не побачив там якихось прикладів бізнес логіки, чи чогось такого, з ооп, які б мені відкрили очі. «Правильні» колекції, ну таке

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

нет никакой борьбы,
select o.order_id, sum(i.amount)
from orders o, items i
where i.order_id = o.order_id
group by o.order_id

И что ты этим хотел показать ? Что на каждый запрос собираешься сканить таблицу с индексами ? Есть и другой подход, когда сумма уже просчитана и ничего сканить не надо. В документе хранить поле Sum и при сохранении ордера добавлять в Sum прайс заказа.

если заказ статичен, то да. но я как-то сталкивался с shopping — очень редко такое бывает. обычно корзину заказов набивают, а потом проводят
я уж молчу что прийдется отдельными полями хранить — item_count (аналитика), paid (аналитика), item_xyz_count (аналитика) ну и до бесконечности

В смысле статичен ? Заказ добавляют к Sum прибавляешь, заказ удаляют из Sum отнимаешь. О плюсах минусах мне писать не надо, я все выше уже написал. Гибкость против скорости работы.

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

О плюсах минусах мне писать не надо, я все выше уже написал. Гибкость против скорости работы

увы. нет ни гибкости ни скорости. в выше приведенном примере представьте что оказалось что item_xyz не достаточно, и надо часть заказов сделать put-aside
п.с. sql собственно и родился потому что nosql тормозил и глючил (притом я в обязательном порядке использую nosql (memcached/redis) для хранения сессий и кешей в любом проекте)

Прелестно. В первом предложении написано что noSQL тормозит а во втором что используешь memcached/redis ... для чего ??

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

sql собственно и родился потому что nosql тормозил и глючил
Прелестно. В первом предложении написано что noSQL тормозит а во втором что используешь memcached/redis ... для чего ??

there no silver bullet ... neo
я использую то что эффективно, там где оно эффективно

я даже не удивлен вашему комменту. тру инглиш вы тоже не совсем осилили гг

кстати, просто в рамках образования, я рекомендую очень, почитать тексты трама (хоть он и придурок, имхо). там гугль транслейт ломается, но это качественный «великосветский» инглиш , которым общаются stackeholders и owners (ну если вам повезет с ними общаться)
п.с. я даже не особо на ваш nosql обижаюсь, когда-то зарабатывал, переделывая на sql, так что считаю nosql это opportunity а не enemy

я (персонально) вообще не парюсь, я ноунейм прогер из ноунейм страны (редкий мой начальник покажет где я живу на глобусе).
и когда я пишу stack-e-holder меня все понимают, в єтом есть своя поэззия
но вы , шура, пилите гг (да. маленький плюсик вы заработали, на stake гг), это говорит, что не все потеряно

но кстати напомнили, самый эпичный заказчик у меня был , когда индусы впарили пиндосу четкий eav en.wikipedia.org/...​ity—attribute—value_model
клянусь, вот один в один вся база — nosql поверх mysql.
я конечно пофыркал, но помог, оптимизировал им в 10x там и сям, и вроде схавали, потом приходят, а вот у нас удаление медленное, ну говорю аф коз, че ему быстым быть если у вас «документы» на 10к аттрибутов , либо помечайте их удаленными и потом тихонько кроном удаляйте, либо терпите....
обиделись . потом ломились в личку, но я тоже обидился — sorry, busy with other projects

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

еще раз ? там будет тоже самое nosql придумали в 60-х годах, sql на 30 лет позже

История это не когда придумали, а зачем придумали. И я вас уверяю там нет «noSQL тормозил и глючил». Когда придумали SQL системы вообще сами по себе стали на порядки работать медленнее. Но были другие плюсы. Потому история пошла по этому направлению. А сейчас прибавилось данных и закон мура почти остановился и возвращаются к noSQL взад, приоритеты стали другими.

еще раз, sql придумали что бы агрегатные функции быстро работали (если просто) , ибо в nosql они работали отвратно

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

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

увы. будет работать медленнее (пересчеты агрегатов) и оверхеда больше (код ради кода)

Вот простой пример.
Анемичная модель.
Контора 1 выдает справку с пропиской
Контора 2 выдает справку о несудимости
Контора 3 выдает справку о разрешении на работу на основе справок из Контора 1, Контора 2.
Итого, юзеру свою анемичную справку нужно нести из конторы в контору.

Рич модель.
Контора Х чекает прописку, несудимость и сразу дает разрешение на работу.

Так где быстрее ? Где оверхеда больше ?

Рич модель.
Контора Х чекает прописку, несудимость и сразу дает разрешение на работу.

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

Так где быстрее ? Где оверхеда больше ?

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

поэтому то теоретики топят за рич модель
им так моделирвоать проще.

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

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

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

да хоть стопицот видов
они же аменичные :)

а если рич, то да, беда, придется серьезно рефакторить.

Как самый яркий пример — переходный момент документооборота что длится годами или даже десятилетиями.

потому что они обычно — рич а не анемик :)

Что случается если сценарий обработки документа под какой-то юз-кейс

вообще-то это типичная задача, это не если, а — всегда. постоянно. еженедельно, если не ежедневно.

да хоть стопицот видов
они же аменичные :)

ну и закончится всё постоянной копипастой чего-то типа: если документ по форме ИБД-3 — то обрабатываем так-то, если есть такой-то атрибут — обрабатываем по-другому, а если это вообще ИБД-5 — то третья процедура вызывается.
Когда добавляется ещё и ИБД-7 — то снова нужно идти по всем местам и ни одного не потерять добавляя очередной if

ну и закончится всё постоянной копипастой чего-то типа:

если мозг отсутствует то да.
при использовании любой парадигмы :)

если документ по форме ИБД-3 — то обрабатываем так-то

вообще-то это это в рич модели.
в анемик документ «исчезает».
конечно остается худой объект.

то снова нужно идти по всем местам и ни одного не потерять добавляя очередной if

по каким всем местам?

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

причем. тут парадигма тоже не при чем.
выше оппонент на примере рич парадигмы показал тот же подход.

это азы программирования :)

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

вообще-то это это в рич модели.
в анемик документ "исчезает«.

модулируя предметную область в которую включен документооборот, если документ — это бизнес-обьект и бизнес-термин — то никуда он не исчезает. В анемичной модели, как чаще всего и бывает так даже наоборот — при появлении новых форм с новыми атрибутами эти атрибуты просто добавляются в тот же класс ибо: «а зачем мне городить тут 100500 классов» © (tm). Богатая же модель заставляет разные сценарии выносить в разные подтипы. Иначе — это движение к анемичности.

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

если эта обработка одна — то и пачка if’ов будет одна. А если это, к примеру,

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

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

да.
со всеми проблемами — стопятого наследника класса, Барбары нашей Лисков и т.п.

и абстракциями которые текут.
и потекут, 101%
это ж бизнес а не математика :)

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

если эта обработка одна — то и пачка if’ов будет одна.

ее не обязательно делать — одну

та же история — делать пачку if’ов в модели, а на разносить по подтипам.
видел такие классы в коде, якобы рич.
так что, это «богатая модель» виновата?

то копипасту ифов придётся городить во всех этих местах.

почему :)
и я обхожусь без копипасты, и кучу кода видел на анемик без копипасты, но вы настаиваете что ее нужно делать!
а я не хочу, не делал и не буду делать!

и не переубеждайте что правильный анемик — это пачки if’ов, и их копипаста.
я лучше тогда буду неправильный анемик делать :)

если документ — это бизнес-обьект и бизнес-термин — то никуда он не исчезает.

я не так выразился, а вы радостно не так поняли :)

В богатой модели поскольку каждый бизнес-сценарий(форма документа) — это отдельный подтип с только необходимыми для работы данными

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

В rich domain model есть несколько недостатков:

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

* Необходимость передавать зависимости в экземпляры объектов. Особенно это весело в сочетании с ORM. Либо добавлять внедрение зависимостей в ORM, либо использовать отдельные модели для маппинга таблиц и для предметной области (это не всегда плохо, но часто излишне)

* Необходимость поднимать объект в память для вызова операции. Для добавления строки в заказ нужно загрузить заказ в память. С одной стороны, после добавления строки в анемичной модели заказ придется перезапросить из базы, а в богатой модели все вычисления уже сделаны в памяти, с другой стороны в анемичной модели будет короткая транзакция, т.к. обновление будет происходить на актуальных данных в базе, а в богатой модели нужно использовать оптимистичные блокировки, иначе легко получить неконсистентность на часто изменяемых объектах. Конечно же, можно и в богатой модели обновить сразу базу, а потом перечитать, но это влечет за собой сразу несколько проблем, в том числе инвалидацию графа лениво загружаемых данных, плюс ОРМ должен уметь перегружать данные в тот же экземпляр объекта (либо, опять же, писать отдельную модель для DAL) . Ну и это опять же будет уже не совсем rich domain model.

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

Ну и нафиг оно нужно?

прекрасное описание почему я решил, что java не мое гг

* Неопределенная цена вызова метода.

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

* Необходимость передавать зависимости в экземпляры объектов.

Не нужно так.

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

Если при этом нужно пересчитать сумму заказа, то его все равно нужно грузить в память. Это часть логики заказа.

Вот можно почитать blog.ploeh.dk/...​1/asynchronous-injection. Часть про C# в фунциональном стиле игнорить ибо треш.

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

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

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

Ну на мой взгляд, пример в статье весьма с натяжкой подходит под понятие rich domain model.

Может я и не прав, но говоря rich domain model я имею ввиду, что логика предметной области (в виде методов) расположена в объектах сущностей.

Грубо говоря, когда мы хотим подтвердить заказ, мы запрашиваем из репозитория (или сервиса) заказ, и у него вызываем метод Accept().

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

Да, так и есть. Но просто представь, что MaîtreD — объект, который мы тоже читаем из БД и у которого есть какое-то состояние.

Тогда все будет в принципе все по-другому, и появятся все недостатки из моего исходного комментария (зависимости, которые должен инжектить ОРМ и т.д.).

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

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

В анемичной модели мы их запрашиваем отдельно (через сервис либо репозиторий). В rich модели мы по идее должны воспользоваться методами самого объекта, но тогда нам приходится передавать репозиторий как зависимость в сам объект, либо мы можем запросить эти данные отдельно до начала операции (например, как в вашем примере, с processing/persistence separation) и передать операции как параметр — но тогда теряется смысл рич модели.

Тогда все будет в принципе все по-другому, и появятся все недостатки из моего исходного комментария (зависимости, которые должен инжектить ОРМ и т.д.).
Проблемы начинаются, когда операция должна для своей работы запросить либо сохранить дополнительные данные.

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

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

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

Я уже писал ниже Dmitry Bugay dou.ua/...​rums/topic/27753/#1620136

Что делать, если, например, сумма заказов зависит от скидки, а скидка, например, от общей суммы, потраченной человеком в магазине?

(То есть, в общем случае, когда инвариант зависит от внешних данных.)

Предыдущие заказы не являются частью агрегата «Заказ», и для выполнения операции ДобавитьСтрокуЗаказа будут нужны предыдущие заказы (ну или их сумма, это не принципиально).

Я правильно понимаю, что преимуществом rich модели вы указываете постоянную (а не eventually) поддержку инварианта?

В rich модели по идее мы должны писать:


void AddItem(OrderItem orderItem) 
{ 
        var allOrderSum = this.Customer.Orders.Where(i => i.Id != this.Id).Sum(i => i.Amount);

     if (allOrderSum > this.Settings.DiscountThreshold) 
    {
          ....
    }
}
Вы согласны с этим? Если нет, то какие характерные отличия рич от анемик?

Инварианты — да, эти данные должны меняться атомарно — агрегат — это границы транзакции. Инварианты должны быть правильным в любую единицу времени, поэтому eventual consistency тут не подходить. Нужно решать с бизнесом что можно сделать асинхронно, а что атомарно.

Что делать, если, например, сумма заказов зависит от скидки, а скидка, например, от общей суммы, потраченной человеком в магазине?

(То есть, в общем случае, когда инвариант зависит от внешних данных.)

Я же уже говорил. Получить все данные, необходимые для операции на самом верхнем уровне и передать все эти данные в модель для выполнения операции.

Если нужна информация по всем предыдущим заказам — наверное, стоит задуматься о выделении сощности СуммаВсехЗаказов и агрегировать данные в ней. Или построить Read Model для получения этих данный. Что по сути одно и то же. И эти данные передавать в Агрегат при добавлении элемента в заказ.

А якщо десять типів знижок, і яка застосовується залежить від стану заказу після додавання чергового товару, і от одна з них базується на якихось підрахунках попередніх замовлень клієнта — теж «отримати всі дані наперед» ? При процедурному/анемічному/транзакшнскриптовому підході проблем нема, посилаєш запити по ходу виконання процедури і все. А в річ моделі ? Інжектити в агрегат сервіси, які лізуть в бд ?

Інжектити в агрегат сервіси, які лізуть в бд ?

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

яка застосовується залежить від стану заказу після додавання чергового товару

Это может решить сам агрегат.

одна з них базується на якихось підрахунках попередніх замовлень клієнта

Да, эти данные получить заранее и передать агрегат.

При процедурному/анемічному/транзакшнскриптовому підході проблем нема, посилаєш запити по ходу виконання процедури і все.

Поэтому процедурный подходи и проще в простых кейсах.

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

Конечно. И что мешает создать такой объект заранее и передать его в агрегат для получения скидки?

Хоча б тим, що застосована буде єдина знижка, а ще до того, як буде прийняте рішення яка, і чи буде взагалі, вже потрібні дані для всіх можливих об’єктів, які можливо будуть потрібні в скопі навколо агрегата, який їх юзає.

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

які можливо будуть потрібні в скопі навколо агрегата

А разве заранее нельзя определить что будет нужно? Почему бы заранее не подготовить объект с информацией по скидкам для текущего заказа и не передать его в заказ?

Це ж бізнес-логіка. Там все, що завгодно, може бути. Якщо оплата бонусними картами знижки нема, якщо у клієнта фіксована знижка, то вона, інакше інша знижка від суми замовлень за минулий місяц...

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

для оформления заказа нужно слазить в 5 мест

Кто, какая сущность должна знать об этом?
Та что запускает логику заказа?
тогда что останется в самом заказе — сложить 2+2?

сам заказ знает про эти 5 мест?
ок, пусть знает.

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

куда поместить это знание?
в сделку?
копипастой?

а, выносим в сервисный слой, и инжектим в заказ и сделку
(типичные конструкторы в Magento видели?)

так что остается И в сделке?
2*2?

Я вижу, что адепты анемичной модели считают, что сервисов быть не должно и сам объек должен хендлить HTTP запрос и лазить в БД. И это не смотря на то, что я шарил два отличных ресурса по теме. Ну, ок. Скажем, есть CommandHandler. Он вытягивает из БД заказ и все, что нужно заказу для обработки команды. Вызывает метод запроса и передает ему нужные данные. Если запросу при этом остается только сложить 2+2 в чем проблема? Но вряд ли ему больше ничего не останется. Иначе зачем для такой простой задачи городить rich model?

а я уж много лет вижу что адепты рич модели просто не знают о чем речь :)

выше писал

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

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

Он вытягивает из БД заказ

понимаете, если б в системе были только заказы, то да, нафик не нужна анемичная модель :)

но обычно кроме заказа, там еще много чего есть.

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

ну правильно.
в итоге заказ просто складывает 2+2, или через if else 2+3
ему уже все предоставили

сервис какой-то собрал ему все что нужно.

то есть почти вся бизнес-логика утекла из модели заказа в этот сервис.

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

то есть почти вся бизнес-логика утекла из модели заказа в этот сервис.

Вот тут логическая ошибка. Не бизнес-локига, а логика получения данных. А логика работы с данными — в самом заказе.

заказ стал худеть. знает все меньше, делает все меньше.

Так ему и нужно знать только то, за что он отвечает. Или для вас rich model — это god object которые все знает и все умеет?

Не бизнес-локига, а логика получения данных.

а логика получения данных обычно тесно связана с бизнес-логикой

хотя по причине оптимизации количества получаемых данных:
если нам по бизнес-логике известно, что для такого типа заказов такие данные не нужны, то и ставим прямо where кроме вот этого и этого

А логика работы с данными — в самом заказе.

ну да, 2+2 конечно где-то нужно сделать :)

Так ему и нужно знать только то, за что он отвечает.

ну вот он и отвечает за перемножение по формуле

формулу да, он знает :)

Или для вас rich model — это god object которые все знает и все умеет?

а для вас anemic model это не объект а структура данных? ;)

вы придумали себе что «адепты анемичной модели» идиоты, и теперь уверились своим вопросом что они таки идиоты? ;)

ну ок, вы постигли суть многолетнего холивара :)

Да, я в курсе, что ты мастер обобщений и развешивания ярлыков. Проектирование домменой модели — это сложно, далеко не всегда нужно и не все в нее могут. Не заходит — не стоит переживать и доказвать, что anemic model — единственно верный путь. Или стоит, как больше нравится.

Да, я в курсе, что ты мастер обобщений и развешивания ярлыков.

если человек задает вопрос как идиоту — то кто-то из них точно идиот :)

Проектирование домменой модели — это сложно

при этом проектирование домена вполне ложится на инженерную реализацию в виде — анемичной модели.

сложно конечно, нужно кроме аналитика быть, или звать еще и инженера :)

Не заходит — не стоит переживать и доказвать, что anemic model — единственно верный пусть.

когда и где я такое доказывал :)

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

эт нормально :)

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

за что, с кем?
ну ладно, пусть борются :D

Тому, що знижка, яка залежить від суми попередніх замовлень може раз на сто заказів спрацювати. А діставати ці дані будете завжди.

Ну, да, все верно. Достали и передали заказу. Я не вижу расходжений. Я же уже неоднократно писал это.

В том, что бизнес-правила реализованы в cущностях, агрегатах и value object-ах, а не в сервисах.

Я бы сказал, что бизнес правила реализованы и там, где вы написали, и в сервисах.

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

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

Я бы сказал, что бизнес правила реализованы и там, где вы написали, и в сервисах.

Если правило нельзя явно отнести как какой-то сущности, то, да, его реализовывают в сервисе.

Потому что логика доставания данных — это тоже бизнес логика

Обычно это Query и QueryHandler.

сайд эффекты (вызовы внешних сервисов) — это тоже бизнес логика.

И выполняются они асинхронно, а не являются частью локальной транзакции.

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

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

Если интересно, вот есть отличный пример реализации с rich model github.com/...​sAS/WorkshopEventSourcing. Там про CQRS/ES, но идеи можно применять не только для ES. Тоже немного нужно въехать, так как не все сразу очевидно.

Причём, по иронии судьбы, джавистам-то, как раз, завезли отличнейший AxonIQ, где есть и CQRS, и ES, и DDD.

Но, какое там — у подавляющего большинства тех, с кем доводилось общаться, Spring Boot наше всё :(

Тобто діставати для операції в 99% непотрібні дані просто тому, що концепція така — як мінімум не аргумент, який заслуговує на увагу ?
А зменшити кількість товару при додаванні в заказ — меседж кидати ?

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

Для чего доставать ненужные данные? Есть же конкретный заказ.
Передали все эти данные в заказ и обработали.

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

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

итого, имеем «копипаст знания»:

о таких клиентах знает И сервис И заказ.

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

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

не?

либо, инжектим в заказ не данные, а провайдеры данных, которые дернет заказ, в зависимости от своей бизнес-логики.
но тогда... заказ становится похожим на сервис, не?

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

Это если проектировать как-попало.

да это понятно что проектировать — это тождественно выбирать рич модель :)

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

о создается отдельная сущность, которая знает какие скидки предоставлять конкретному пользователю.

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

И инкапсулирует эту логику.

да.
так при движении от рич к анемик и происходит — модели сущностей предметной области становятся все тоньше и тоньше.

Еще раз, я не пытаюсь переубедить тех,

еше раз, не надо приравнивать проектирование к выбору конкретной парадигмы.

кто не хочет принимать другие точки зрения

ваша точка зрения мне известна с конца 90ых

называется она:
ООП это когда мы напрямую мапим объекты реального мира на классы.

правда, и вы сами уже знаете что это не есть хорошо, и предлагаете отказаться от нее:

то создается отдельная сущность

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

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

так что вы на пути к анемик парадигме

в анемик так все и раскладывают :)

и ООП — это не обязательно пытаться классами языка программирования описать классы предметной области.
как и поведение объектов предметной области не обязательно описывать в объектах языка программирования.
можно конечно. бывает и полезно.

но — не обязательно.

но вы считаете что обязательно.

спросите игроделов, как они используют С++, и почему они категорически не моделируют игровые объекты в классах С++
не умеют программировать? проектировать не способны?

а может просто кое-что знают, например о цене cache miss?

тоже самое с работой с реальной БД
конечно, конечно, можно развернуть кластер на AWS, и т.п.

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

только вот да, не рич, не DDD
ужас!

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

где, в анализе бизнес-аналитика?

или программисты себе ее измыслили?

Извини, все неосилил

извиняю.
это нормально :)

потому что обычно так считают о других

или не хочет выходить из своей зоны комфорта и посмотреть как еще можно.

те кто уверился что схватил Бога за бороду,
и потому просто вцементировался в эту свою зону кофморта

а в жизни то — серебряной пули не существует...

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

что конеретно мне не нравится в anemic модели

ну мне в целом в рич все нравится.

просто видел как в чужих проектах, так и в своих — как от нее уходят. и главное ПОЧЕМУ. почему этот процесс — естественный.

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

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

правильно.

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

то же и с рич модел — обчитался DDD и давай применять.

проектировать то еще не научился :)

паттерны ООП еще не разлюбил :)

но пока старший джун этого не прошел, он конечно уверен что постиг бест практики, и ужо теперь то задаст всем!

Ай малацед, утер нос так утер 😉

Самостоятельное проектирование — это когда ты знаешь правила которые нарушаешь, и можешь рационально объяснить почему ты пошел на нарушение правила

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

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

правда неграмотные плюющие на традиции — и избы не построят :)

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

а о самостоятельном — невозможно такого сделать.

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

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

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

Был такой эксперимент, Р. Гельда (Held R) и А. Хайна (Hein A) с котятами.

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

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

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

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

с вопросом, в уме конечно :)

так ты какой котенок — тот что тяжко тягает тележку, или тот что весело и непринужденно наблюдает, и верит что все то он понял

Мне нравится как ты навязываешь мне свою игру 🙃.

P,S.

да, а вы в курсе что бизнес-логика во многих тяжелых энтерпрайз проектах утекает с годами даже не в анемик, а на сторону БД, и переписывается на PL/SQL где вообще предметная область в коде не просматривается :) потому что язык, ой и ах.

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

подумайте, может это все же вы не способны услышать иную точку зрения?
;)

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

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

Перечитайте, писал и как нужно тоже.

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

Если нужна информация по всем предыдущим заказам — наверное, стоит задуматься о выделении сощности СуммаВсехЗаказов и агрегировать данные в ней. Или построить Read Model для получения этих данный. Что по сути одно и то же. И эти данные передавать в Агрегат при добавлении элемента в заказ.

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

Он вообще-то прав, увы.

Хмм, а процитированный текст не о том, как нужно? Или ожидалась рабочая демка? В блоке марка, вроде, такой пример тоже разбирался, используя визитор для рассчета скидки.

о выделении сощности СуммаВсехЗаказов и агрегировать данные в ней.
агрегировать данные

Как?

Подписываться на события заказов и обновлять данные. Похожим образом как и проекции строятся в Event Sourcing.

Хмм. Я думал об этом уже чуть ли не все знают. Сегодня это практически мейнстирим. Вот из твоего примера OrderCancellation обычно моделируют в виде события OrderCancelled. Это событие кладется в стрим событий, например Event Store или Kafka. Любой может подписаться на это событие и обновить свое состояние. Если и правда про это не слышал можно попгулить Event-Driven Architecture.

Это событие кладется в стрим событий

Как?

Любой может подписаться

Как?

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

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

Кто-то просто смотрит на моделирование сквозь призму Java Enterprise и не может взглянуть на вещи под другим углом. СуммаВсехЗаказов будет формороваться асинхронно и просто вычитываться из бд. При чем тут DI контейнер? Ладно, извини, дальше не интересно.

будет формороваться асинхронно и просто вычитываться

Ага. Нью-Васюки ))

Хмм, неужели люди из Java Enterprise на самом деле не смотрят какие подходы еще есть вокрут.

Смотрят, это вы не смотрите, что другие посмотрели, оценили, кое-где применили, но не согласны с мессианством уверовавших в спасение адептов.

Звучит как аргумент ценителей SVN попробовавших и кое-где применивших Git.

Я уже сказал в этом треде, что успешно применял рич модел. И я покушал и плюсов и минусов.
При этом я не согласен с проповедями Бугаенко о том что это единственно истинное ооп.

Т.е. ты мне все это время пытался объяснить, что тебе не зашел Бугаенко? Спасибо за инфу.

Андрей, ну представьте себе ситуацию, когда бизнес-логика не может запросить все нужные данные до (ну либо же может, но это равноценно дублированию алгоритма этой самой логики).

Остается два варианта: разделять операцию на две части (загрузку и выполнение), либо передавать зависимости в модель/сервис/то-что-будет-выполнять-работу.

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

В рич модели как бы вы сделали?

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

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

Мне, кажется, я об этом тоже уже писал в этой теме.

Вы в каждом сообщении пишете, что вы уже это писали, сорри, но я не могу запомнить все ваши сообщения :)

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

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

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

Смотрите, но уже одно это приведёт к существенному усложнению системы. В каких-то случаях, да, такой подход оправдан — например, допустимость асинхронности сайд-эффекта, или необходимость повторять попытки «до победного конца». Другими словами, обработчики событий оправданы и хорошо работают для сценариев наподобие «отправить email с подтверждением» или «уведомить через вызов Web API внешнюю систему».

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

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

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

уже одно это приведёт к существенному усложнению системы

Если система работает, масштабируется и ее легко поддреживать все эти усложнения не нужны. Я уже говорил, что я это уже говорил 🙂?

Должна ли операция обработки заказа отвалиться, если после обработки и до коммита транзакции отправка имейла отвелилась?

А зачем вообще может понадобиться отправлять имейл до коммита транзакции?

Пример кода Dmitry Bugay:

OrderCancellation cancel = order.cancelUsing(additionalData);
orderDao.save(order);
sideEffectService.doOn(cancel);
Отправка имейла — сайдеффект, в дугих сайдэффеках буду какие-то записи в бд. Все это заворачивается в одну локальную транзакцию для строгой консистентности. И вот как раз отправка имейла или отправка сообщения в брокер не могут быть выполнены транзакционно, хотя реализованы в сайдэффектах. А транзакция закоммитится только после того, как все сайдэффекты успешно отработают.

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

все так.
но решение не зависит от выбранной парадигмы декомпозиции предметной области.

в рамках комита должна происходить и регистрация на выполнение сайд эффекта.
и только после коммита — его обработка.

правильных, проверенных вариантов реализации несколько штук.
для разных выбранных парадигм :)

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

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

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

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

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

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

На сколько требования корректны и обоснованы.

а для этого вас исполнительные директора и отделы маркетинга должны поднять до своего уровня компетенции.
послав на пару лет в соответствующий колледж :)

Попробовал бы построить модель по-другому.

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

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

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

но проблеме то сто лет в обед :)
и на практике эта проблема — решаемая.

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

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

Просто потому что так проще ...

бинго!

но искать простые пути — зазорно, да?
надо наворотить?

а задачу можно решить с менее квалифицированными кадрами

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

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

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

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

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

И так большинство проектов и разрабатывается.

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

и поэтому так они и будут разрабатываться.

да и просто потому что
абстракции текут.

какие б не выбрали — потекут «по определению».
при проектировании И это надо учитывать.
в действительности реального мира.

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

У тебя навязчивая идея комментить каждый мой пост?

При всём уважении, это отвечает на вопрос «как строить нужную для расчёта скидок read model», и только на него.

Вопрос же, вызвавший пик холивора, звучал так:

Как наиболее эффективным способом передавать в сущность Заказ информацию, необходимую для расчёта скидки, если:

  1. Вычитка данных, необходимых для расчёта скидки, таки создаёт дополнительные накладные расходы — и её лучше избежать, если в данном конкретном случае расчёт скидки неприменим
  2. Сама сущность Заказ, очевидно, не может запрашивать дополнительные данные, иначе в неё придётся инжектить какой-нибудь репозиторий, что противоречит идеям DDD / Clean Architecture

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

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

Вот ту самую Read Model опрашивать и передавать информацию в агрегат. Который будет принимать решение о скидках, например. Или выделить отдельную сущность для рассчета скидок.

Мне непонятно что за информация такая, которую нельзя узнать заранее и можно будет получить ее только при процессинге заказа.

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

Мне непонятно что за информация такая, которую нельзя узнать заранее и можно будет получить ее только при процессинге заказа.

Узнать, наверное, можно, но цена вопроса... Например, из всего ассортимента в 100500 позиций есть, скажем, 50 позиций по которым предоставляется доп. скидка клиентам, которые за пошедние Х месяцев уже купили этой номенклатуры на энную сумму («механика» оффера странная, но в битуби энтерпрайзе и не такую дичь можно встретить).

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

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

Да все верно. Мы можем спросить у заказа какие в нем есть товары, спросить специальные цены на эти товары. И передать эту инфу заказу в обработку. Если это звучит как over-моделирование можно писать как привыкли. Я просто привел пример того, как можно улучшить дизайн и поддерживаемость системы.

Как бы вы реализовали такой кейс на Haskell? Не думаю, что помечали бы все функции как IO.

Как бы вы реализовали такой кейс на Haskell?

бинго!
ну во, и до Haskell’я добрались, как до последнего аргумента тру программиста!

ну млин, столько лет, а у всех трушных одна и та же методичка для холивара.

Но тут даже дискуссия не о объектном/пороцедурном стиле, а о рассчете скидки работая над объектами в памяти без необходимости запрашивать инфу в процессе расчета.

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

утверждение что мне может понадобиться любая инфа говорит о просто непродуманном дизайне без понимания предметной области

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

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

Поэтому, есть ощущение, что rich model / DDD применимы и оправданы там, где

а) Есть нетривиальная бизнес-логика
б) Требования к ней более-менее стабильны и могут быть хорошо проанализированы заранее

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

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

если завтра от заказчика прилетит change request, в корне меняющий ту же пресловутую логику расчёта скидок

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

Значит или заказчик не знает чего он хочет

о, еще один аргумент тру программиста
юзверы тупые, закаказчки дурачье :)

какой оборот у вашего бизнеса, о гений?
;)

Осталось только захрюкать от радости.

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

А если брать стартапы, так там поиск ниши с постоянным пивотингом — это совершенно нормальное состояние.

Таких заказчиков — пруд пруди, хотя бы потому

конечно пруд пруди :)

владельцев автомобилей автомеханики тоже считают идиотами.
а врачи — пациентов :)

что далеко не все из них умеют / хотят в качественный бизнес-анализ.

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

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

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

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

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

почему же тогда не отказаться от таких заказчиков?
пусть какие-то индусы ими занимаются!
и пусть заказчик разрыдавшись не вернется с покаянием :)

бизнес и за рефакторинг платить не хочет :)

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

а) Есть нетривиальная бизнес-логика

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

Поэтому, есть ощущение, что rich model / DDD применимы и оправданы там, где

а) Есть нетривиальная бизнес-логика
б) Требования к ней более-менее стабильны и могут быть хорошо проанализированы заранее

да, так и есть

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

так и проектируются ERP системы

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

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

ну или в ТНК.
правда там наверняка места архитекторов уже заняты, и системе может быть больше 10ти лет, и переписать ее никто не даст :)

да, чел так и не понял, темы топика :)

и рассказывает нюансы реализации при применении рич моделирования

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

но для чела ничего кроме лыж не существует
кто не лыжник — тот олух! его уверенное мнение :)

blog.ploeh.dk/...​04/structural-inspection. Если есть несколько правил для предоставления скидки, надеюсь, получится их скомпоновать самостоятельно?

о, визиторы!

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

а код хороший, да.
как пример того что

выбрал такой-то общий подход тебе придется решать вот такие проблемы.
выбрал другой — будут другие

но только зачем альпинисту мазь для лыж?
лыжнику конечно она нужна.

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

по тому же сценарию:
какие-то частные, специфические решения принятые у лыжников выдаются за вершины проектирования :)

Во ты упорот 🙂. Та хоть на перфокартах дырочки делай. Это длительное кодописание на адинэс такой отпечаток накладывает?

1C как платформа реализована как рич платформа :)

приходилось поэтому пару раз писать работу с ее таблицами в обход движка.

но вы да, плюйтесь в лучшее в СНГ решение основанное именно на рич моделировании :) в отличие от например SAP/R3

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

там их кажется три типа?
автор кода фабрики визиторов то предлагает?
нельзя ж их вот так просто, с помощью new делать.
не кашерно ведь.

а до фабрик фабрик — еще не добрались?

ну да, конечно :)

вы не отличаете проблем проектирования(уходящих в глубины гносеологий с эпистемологиями) от проблем кодирования (реализации), которые порождены ограничениями аппартаных средств — но претендуете указанием абрревиатур и терминов показать что вы то дизайн умеете :D

это нормально, это естественно.

Нужно больше оценочных суждений.

А в річ моделі ? Інжектити в агрегат сервіси, які лізуть в бд ?

Вивсеврети, рич модель идеальна. А ты просто ниасилил. ©

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

какая разница что оно делает под капотом? Оно же его не на твоём потоке исполнения делает. Если метод возвращает любой из вариантов фьюче-промисов (CompletionStage в Java) — значит он тяжёл.

Необходимость передавать зависимости в экземпляры объектов.

есть такое. А в чём проблема то? В чём излишество?

Необходимость поднимать объект в память для вызова операции.

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

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

Ну и нафиг оно нужно?

коротко — чтоб не писать if instanceof, не разкопипащивать этот if instanceof по всему проекту, уменьшать rigidity и fragility кода.

есть такое. А в чём проблема то? В чём излишество?

Проблема в смешивании в одном объекте бизнес-данных и зависимостей.

коротко — чтоб не писать if instanceof, не разкопипащивать этот if instanceof по всему проекту

Написал десятки проектов с анемичной моделью, и ни разу не сталкивался с необходимостью проверять типы во время выполнения (исключая неявные проверки в реализации сериализации, ОРМ и тд)

уменьшать rigidity и fragility кода

Бла-бла, каким образом богатая модель это уменьшает?

Проблема в смешивании в одном объекте бизнес-данных и зависимостей.

так зависимости же спрятаны за портами (контрактами) что сама же модель для своего существования/функционирования требует

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

if (’foo’==element.getType()) - по сути то же самое. Но если и этого не было — ok, простейший, примитивный, книжный пример на OCP Violation: у вас есть графический контекст и коллекция элементов типа Shape. Нужно отрисовать композицию. В случае с объектами (инстансами классов что сами несут в себе операции по их отрисовке и данные о том где эту отрисовку нужно совершить) принципы не нарушаются и код приложения не пестрит одинаковым набором if’ов. Всё решается полиморфизмом. В случае дтошки+процедурки — нужно в вызывающем коде понимать какую процедуру (сервис) вызвать — пестрит ифами и всё это расползается по всему приложению.

каким образом богатая модель это уменьшает?

не сама богатая модель уменьшает, а использование «Tell don’t ask» и направление зависимостей внутрь. Богатая модель — скорее следствие.
Просто пока у вас данные не инкапсулированы — вы и полиморфизмом в полной мере пользоваться не можете. А в анемичной модели ни о какой инкапсуляции и речи не идёт.

у вас есть графический контекст и коллекция элементов типа Shape. Нужно отрисовать композицию.

вот-вот

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

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

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

А в анемичной модели ни о какой инкапсуляции и речи не идёт.

это с чего взялся такой вывод :)

только с того что в анемичном подходе инкапсулируется другое чем в рич?

пестрит ифами и всё это расползается по всему приложению.

а при чем тут парадигма :)

Всё решается полиморфизмом

полиморфизмом — чего :)

полиморфизм никак не навязывает — декомпозицию классов

рич и анемик — это варианты подходов к декомпозиции.

А в анемичной модели ни о какой инкапсуляции и речи не идёт.
это с чего взялся такой вывод :)

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

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

ок, на этом ветку закроем.

В случае анемичной модели, поскольку это всё дтошки

в анемичной модели не DTОшки.

но как и с аджайл коучами и с байками о водопадной модели нет смысла дискутировать,
так и с придумавшими эту байку из метафоры о dtoшкакам

пишите как хотите, мне то какое дело

у меня интерес был о постоянсте холивара, его причинах :)

а обучать программированию у меня есть кого, как на работе так и вне :)

ок, на этом ветку закроем.

да, и я об том.

пишите как хотите, мне то какое дело

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

интерес был о постоянсте холивара, его причинах

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

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

в этой ветке каждый обсуждал всякое.

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

мне были интересны стойкие оловяные солдатики, которые топят за нее :)
может у них за эти 10 лет новые аргументы появились?
может они поняли проблемы, и нашли их решения?

а вдруг?

но, чуда не произошло.

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

вот тут да, в целом согласен.

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

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

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

Щоб не бути голословним — для прикладу з Checkout заказа ви будете створювати окремий клас-агрегат для цього кейсу, в якому всі пропертя позицій заказу readonly і їхня колекція не має Add/Remove ?

Возможно. Нужно смотреть на остальной проект и на то что хочет бизнес. Но да, прежде всего предложил бы вариант когда пользователь чекаутит корзину. При этом пользователь — read-only проекция. Только те данные что непосредственно надо. И только на чтение. Без прямого доступа.
Корзина, что наполнялась в совсем других кейсах и была либо только монотонно растущей в одном либо монотонно убывающей в другом именно в кейсе чекаута представляется тоже read-only проекцией представляющей только нужные данные.
Результатом чекаута является заказ в определенном статусе (состоянии). Заказ добавляется в коллекцию заказов. Именно в этом кейсе коллекция заказов — это контракт с единственным методом — добавления заказа.
На слое инфраструктуры эта коллекция реализуется паттерном «репозиторий» в Фаулеровском понимании термина.
Если очень грубо, в первом приближении и вкратце — то да, как-то так.

до речі, реально знаю пацанчика (кєк, йому вже за 40) але
так от, він робив nosql яка з діскєт підгружала дані (писала — вставьтє діскєту номєр 56 щоб продовжити )

Такое далеко не только в Java. Такое везде.

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

В том то и проблема что пока сам пишешь прочитав умные книги — всё отлично. Когда начинаешь искать себе разработчиков в команду — поди найди кто Rich Domain Model умеет готовить, кто умеет в DDD и такое прочее, а проекты нужно выполнять уже на вчера.

Я намерен бороться по возможности с анемичной доменной моделью.

А вы?

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

Где-то на форуме была на тему противостояния моделей фраза которую можно перефразировать как: «Здесь так принято.» Если выпримите что люди не хотят учиться и развиваться, не хотят и не видят смысла рассматривать альтернативы, а хотят штамповать типовые похожие друг на друга решения

Сервис-Дао-Хибернейт

в случае Java — будет гораздо проще.

Если выпримите что люди не хотят учиться и развиваться

Развитие это алгоритмы и математика. А ддд, фаулер, классические 3 кита ооп, паттерны — это все глупые попытки натянуть вообще нихрена нерабочие и даже глупые идеи на реальный мир.
www.yegor256.com/2017/03/28/solid.html
www.yegor256.com/...​oriented-programming.html

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

Вы прослушали тизер к

yegor256

:)

Развитие это алгоритмы и математика.

Озвучте 3 последних (крайних) применения этого за послений месяц.

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

Потому что херня это ваше оопэ

Ваше мнение очень важно для нас.

Так это не мнение, а факт.

medium.com/...​r-disaster-️-92a4b666c7c7

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

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

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

AFAIK, бизнесу

в массе своей

глубоко безразлично на каком языке будут решены его проблемы. Этот тред в частности — доказательство того, что ООП добавляет принципиально не решаемых в его рамках проблем. И тренд на FP таки имеется, как в изменениях в самой Java и Cpp, так и ростом вакансий Scala/Clojure/Rust/Haskell, да и маргинальных Elm, Pure Script, OCaml/Reason с F#.

Go кстати успешнее чем все эти языки вместе взятые. При том что ООП там очень условный. А фп и не пахло.

Потому он наверное и успешный, что в нем нет оопешного булшита.

тренду ФП лет двадцать минимум.
так что он еще долго будет трендом. в холиварах :)

а сами ЯП да, становятся мультипарадигменными.

тренду ФП лет двадцать минимум.
так что он еще долго будет трендом. в холиварах :)

Ну не скажи. Конечно, сами по себе «пуристские» ЯП с исключительно функциональной парадигмой в ближайшее время так и останутся нишевыми.

Но. Сами функциональные подходы в последние пару лет минимум начали активно применяться в коммерческой разработке. Возьми то же functional-reactive programming, которое сейчас в тренде и на фронт-енде, и в мобайл разработке.

понимаете, этот холивар еще древней чем reach vs anemic

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

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

если же копнуть в то к чему модники добавляют слово functional то окажется что к ФП это имеет такое же отношение как маргарин к маслу.

но для этого ж надо говорить об ФП, а не о трендах.

а холивары об ФП обычно имеют к нему такое же отношение как и трындеж к трендам.

хайп — это мягкое описание «тренда ФП» :)

если же копнуть в то к чему модники добавляют слово functional то окажется что к ФП это имеет такое же отношение как маргарин к маслу.

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

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

факт в том ФП не был мейнстримом и не станет

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

просто кругом неграмотные, не умеют готовить, и т.п.

ну ок, ты дартаян, а все вокруг пи-сы :)
факт то где? если неграмотные, которых 95% не могут пользовать такой суперской вещью, а только боги с олимпа — то каков тренд то?

но в том и фокус, что холивар вечен потому что у адептов всегда плохо с логикой :)
пристрастие глаза застит.

чистая психология, никакого отношения к достоинствам и недостаткам технологии не имеющая.

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

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

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

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

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

а то что большинство и ООП не знает, по большому счету, да, все так.
тот кто знает — и становится — меньшинством :)

так вот ФП не было и не станет мейнстримом потому что это именно меньшинство его отбраковывает для массового применения.

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

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

демагогия — это трындеж о чем-то называть трендом это чего-то.

а у фп есть достоинства и недостатки как у технологии.
которые можно обсуждать.
но не с теми кто не в состоянии отличить трындеж от тренда.
и не с теми кто не принимает решения об использовании технологий.
(как в одной статье об этом было:
знаете кто этот джон годами пишущий статьи об фп, выступаюий с докладами по фп...
последних 10 лет — рядовой программист на perl в букинг ком!)

про это и сказал :)

а как копнули глубже

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

всем рекомендю так делать :) ну.... тем кто может конечно переходить между уровнями абстракций.

сразу и видно с кем имеешь дело. ну и тогдаа уже решай, что с ним делать дальше.

Но хорошо хоть не спорите с тем, что в вопросах качественных фп впереди

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

я по ссылке не ходил, но когда-то писал код используя библиотеку которая была реализована на паттерне observer, ох я и намучался , потом с моим кодом еще помучались пацаны из gcc, в конечном счете переписали по человечески гг

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

 то есть, в Котлине и Скале можно продолжать анемичную модель использовать, а в Джаве нет? А если Джава с Ломбоком?

Пошëл бороться с анемичными моделями с мировых подиумов.

Наши вашим [моделями] машут

вот за это я люблю форум, пойду почитаю что это за модель такая, впервые в жизни слышу термин =)

martinfowler.com/...​ki/AnemicDomainModel.html еще о Rich Domain Model как альтернативе можно почитать

И шо ты собираешься бороться? Пойти к тем, кто пишет в нелюбимом стиле и ножнички ему на шею в прыжке? Я тебе скажу так: если сложность предметной области небольшая можно писать как угодно. Если большая, конечно нужно модель выделять. Обычно пишут какой-то внутренней dsl на скале или кложуре

Я тебе скажу так: если сложность предметной области небольшая можно писать как угодно. Если большая, конечно нужно модель выделять. Обычно пишут какой-то внутренней dsl на скале или кложуре

О, эксперты подтянулись. Что аж так не хотят вовкин фриланс покупать, что надо сказки придумывать и постить их на девелоперских форумах?

Написание ДСЛ не является стандартной практикой («обычно пишут») для разработки систем со сложной, а тем более большой предметной областью. Причина тому простая — это дополнительный код который надо поддерживать (тратить ресурсы), разработка и внедрение ДСЛ — это так же затраты времени + дополнительная «блокировка» при разработке новых фич.

В реальном мире ДСЛи если и появляются, то как побочный продукт, то есть написаны ои как правило в виде «удобного АПИ на основном языке разработки».

Стандартный способ борьбы с большими (и сложными) предметными областями — это, как ни странно, разбивка их на более мелкие :)

В реальном мире ДСЛи если и появляются, то как побочный продукт, то есть написаны ои как правило в виде «удобного АПИ на основном языке разработки».

Ну так это ж тоже DSL — embedded DSL — вполне себе популярно и несложно в разработке и поддержке.

Ну так это ж тоже DSL
Обычно пишут какой-то внутренней dsl на скале или кложуре

Не тоже (самое) :)

embedded DSL — вполне себе популярно и несложно в разработке и поддержке.

Популярно пока у бизнеса есть деньги на то чтобы тратить их и не следить за тем на что они идут.
Проблема с ДСЛями в том что «они понятны только их авторам», то есть потери идут при внедрении их в другие команды (еще и команды часто сопротивляются). Затраты в поддержке возникают не из-за сложности, а по тому что при необходимости расширить ДСЛ появляются всякие межкомандные взаимодействия.
На практике ДСЛ хорошо заходят в основном во всяких тестовых утилитах.

Мы сейчас по-моему обсуждаем абстрактные сферические яблоки. Я имел в виду, что некоторые DSL-подобные возможности в языках типа lambda with receiver в Kotlin и extension functions позволяют относительно легко упростить написание кода в доменной модели (если не уходить в дичь типа «send 5 apples to newton», пытаясь построить естественный язык внутри языка программирования). Я не работал с Closure, но имел опыт с Lisp, и хочу тоже сказать что DSL на Лиспе не особо отличался от самого Лиспа (разве что скобочек становилось меньше) :D И это не требует обычно каких-то особых познаний. Про реальные DSL я полностью согласен, что это дичь в наше время.

аж так не хотят вовкин фриланс покупать, что надо сказки придумывать и постить их на девелоперских форумах?

Как ни странно, даже графические покупают.

Обожаю срачики rich vs anemic ещё со времён rsdn-а

there no silver bullet... neo
если структура и логика простая — то дополнительный layer просто бессмыслен (код ради кода)
если у вас всплывают транзакции — то смесь доменных классов и транзакций внутри и снаружи создаст локальный адик
для себя примерно выстроил такой подход — все начинается с простых моделей , а затем по мере эволюции что-то переносится-добавляется в модель, но только если там что то относительно простое (как минимум не требующее своих транзакций), многократно используемое и не увеличивающее связанность между компонентами, но это php, не знаю можно ли такое в java

На словах борцун такой, а гихаб-то твой пустой. В нем совсем нет DDDи. Так сиди, и не...
Я не нав’язую свою думку середнього пограмера, але Rich domain model — це часто боротьба з ооп і оод за побудову чогось ідеального, на що витрачається забагато часу, що легко розвалюється незначними змінами у вимогах, в доменому об’єкті сутності все одне неможливо інкапсулювати все, що з цією сутністю відбувається в системі, що неможна застосувати до апдейта одного поля і багатьох об’єктів, і т.д, і т.п.

Могу сказать, что анемичная модель очень гибкая и легко поддерживаемая, хотя, согласен, доменная модель с логикой гораздо ближе к ООП.
Опять же, всё зависит от задачи. Для веба, где каждый запрос независим и мы не храним в памяти кучу данных, анемичная куда более удобна.

Душы яё двама рукамы

Я намерен бороться по возможности с анемичной доменной моделью.

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

Потом поумнел конечно. Теперь только анемичная модель.

Удивлен увидеть эти две фразы в одном вместе.

Поясню: в частности, смущает поумнел и категоричность в одной логической структуре.

Если отбросить демагогию, то он примерно правильно сказал.
Изза некоторых базисов, именно «анемичная» модель является естественной. А «рич» — как раз неестественной.
Поумнение как раз и выражается в том, что разных пророков вроде Бугаенко нужно принимать к сведению как явление, но жить в реальном мире.

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

В объектно-ориентированной парадигме Rich Model выглядит более ественно.

Ну слушай, я тоже не вчера увидел ООП, не надо ретранслировать стандартные аргументы рич-модел-адвокатов.

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

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

Проблема не в модели как таковой, а в 1) низкоквалифицированных крудописцах с одной стороны и 2) зачастую маразматических требованиях со стороны заказчика. Рич модель гораздо болезненнее реагирует на криворукость девов и «сделайте то не знаю что, но чтобы лягушка превращалась в принца а вот тут кнопочка и тут чтобы скидка».

А анемичная модель позволяет инжектить что-попало куда-попало

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

К слову, один из подпроектов у меня частично использует рич модель. Но только потому, что там в логике есть специфика, которая делала классическую «анемичную» модель неудобной и труднореализуемой. Поэтому могу с уверенностью сказать, что пихать везде рич модель и адвокатировать ее как сильвер буллет и идеал — это ребячество и донкихотство.

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

Я же уже неоднократно писал в этой теме, что в домменную модель ничего инжектить не нужно.

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

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

Я же уже неоднократно писал в этой теме, что в домменную модель ничего инжектить не нужно.

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

Из разных комментов.

Вот можно почитать blog.ploeh.dk/...​1/asynchronous-injection. Часть про C# в фунциональном стиле игнорить ибо треш.
Получить все данные для выполнения операции из бд, выполнить операцию оперируя данными в памяти, сохранить новое состояние в бд.
Если eventual consistency не подходит — тогда все склеивать в один агрегат. Агрегат определяет границы транзации.
Получить все данные для выполнения операции из бд, выполнить операцию оперируя данными в памяти, сохранить новое состояние в бд.

Ты сейчас описал 100% софта в мире. Зачем? Каким образом это относится к инжекции? Самоочевидные и общеизвестные тезисы типа «небо сверху» в принципе не являются доказательством или аргументацией чего-либо.

Если eventual consistency не подходит — тогда все склеивать в один агрегат. Агрегат определяет границы транзации.

Ниочем.

asynchronous-injection

Я сейчас прям представил очередное «спринг говно я напишу свой спринг со своими @Transactional и @Async».

Короче вообще не вижу аргументов.

А суть проста. Любой внешний функционал требует чтобы его заинжектили. ВСЕ является внешним функционалом, потому что чистый new Object() не содержит ничего вообще. Поэтому единственный путь тру-рич-модел-адковатов это

new MyRichObject() {
    new MyRichObjectDependency() { 
         new MyRichObjectDependencyDependecy() {
              ....
         }
    }
}
Любая попытка уйти от этого приводит все ваши умозаключения к 2 путям — первый это обычный конструктор инжекшн, для решения чего и был создан спринг и аналоги, или второй — магия, которая приведет вас к тому же рантайм контейнеру и своему @Inject / @Autowired.

Ох йой. У нас просто очень разное представление. Попробуй почитай blog.ploeh.dk/...​1/asynchronous-injection. Там сходу въехать может быть сложновато, но если зайдет, может поменять немного мировозрение.

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

Однако, попытка натянуть сову на глобус присутствует. Результат попытки тоже присутствует — качественного нового подхода не создано, майндфак в код добавлен, часть ДИ из очевидной стала хренпойми какой.
Цель рич-модел-адвоката достигнута, адвокат доволен.

Ах да, про дебаг такого кода я вообще умолчу.

У нас просто очень разное представление.

Да вполне оно одинаковое. Просто нету аргументов. Если рич-модел-адвокату предложить создать альтернативу

new MyRichObject() {
    new MyRichObjectDependency() { 
         new MyRichObjectDependencyDependecy() {
              ....
         }
    }
}

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

Я говорил, что может быть трудновато. Там ключевая идея в том, что IO выносится на самый верхний уровень, туда, где начинается обработка запроса. Все остальное — объекты в памяти, которые не делают никаких сайд-эффектов. Это и есть доменная модель. А багатая она или анемичная, на самом деле, второстепенно.

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

Стопэ, я против DDD ничего не говорил.

Но DDD это не про анемичная-вс-рич модел, как я понимаю само DDD.

Я говорил, что может быть трудновато

Там нет ничего трудного (разве что шарп код мне как джависту читать не фонтан по религиозным причинам :) ), там есть попытка смотреть в гвоздь вместо микроскопа, причем попытка эта основана не на какой-то конкретной пользе, а на «я художник я так вижу».

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

Это говнокод, а не анемичная модель :)

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

В том же Spring, нам кто-то мешает инжектить репозиторий в контроллер, а в контроллере писать что-то в духе

var order = repository.Orders.SingleOrderByID(orderId);
order.Cancel();
repository.Orders.Save(order);

(в реальности всё, вероятно, будет несколько сложнее в связи с тем, что нередко придётся использовать unit of work, но это специально упрощённый псевдокод, который, всего лишь, демонстрирует идею).

В том же Spring, нам кто-то мешает инжектить репозиторий в контроллер

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

var order = repository.Orders.SingleOrderByID(orderId);
order.Cancel();
repository.Orders.Save(order);

И где тут рич модел? Ей тут и не пахнет. Обычный ОРМ и анемичный процедурный стиль. То, что ты сделал order.Cancel(); вызвав метод на объекте а не на дао/сервисе, не делает твою модель богатой аж никак. Это значит только то, что в рамках твоей бизнес-логики логика метода Cancel() не имеет сайд эффектов, не нуждается в дополнительных данных извне и сохранение измененного состояния ордера будет выполнено позже средствами ОРМ.

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

Является ли анемичным код:

OrderCancellation cancel = order.cancelUsing(additionalData);
orderDao.save(order);
sideEffectService.doOn(cancel);
?

В самом упрощенном случае анемичная модель выглядела бы так:

var order = repository.Orders.SingleOrderByID(orderId);
order.Canceled = true;
repository.Orders.Save(order);
И это говорит лишь и том, что мы понимаем это совсем по-другому.

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

Изначальный посыл богатых моделей и идей вроде егоровских — в (в частности, не только в этом) в отказе от сущностей типа сервисов и репозиториев и реализации всей логики внутри классов доменных моделей. Что требует инжектирования инфраструктрутурных классов в эти самые модели. Что приводит к противоестественным конструкциям вроде new Order(dataSource, eventPublisher, jmsProducer, emailSender);

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

Богатая модель — это про инварианты.

А без вумных слов? )

Например, что при отмене заказа какие-то еще свойства кроме Canceled тоже нужно изменить атомарно.

Драсти приехали. Транзакцию зачем придумали?

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

Драсти приехали № 2. Т.е. на консистентность нам наплевать и асинхронно-атомарный сайд эффект мы выполним даже тогда, когда причины этого сайд эффекта не случилось?

Драсти приехали. Транзакцию зачем придумали?

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

Драсти приехали № 2. Т.е. на консистентность нам наплевать и асинхронно-атомарный сайд эффект мы выполним даже тогда, когда причины этого сайд эффекта не случилось?

Было бы странно обрабатывать событие до того, как оно случилось, не?

Как раз слежу. И вижу непонимание того, зачем придумали транзакции.

Было бы странно обрабатывать событие до того, как оно случилось, не?

О да. А еще очень странно обработать событие, у которого не было причины. Непонятно о чем я? Очевидно что непонятно.

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

Очевидно что непонятно.

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

Можно же выполнить операцию и сохранить событие транзакционно и только после это обрабатывать событие с сайдэффектам асинхронно.

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

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

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

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

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

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

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

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

Что все дискуссии о рич-вс-анемик суть пустая болтология. Как и большинство аргументации рич-модел-адвокатов.

Что те, кто топит за рич против анемика, сами не понимают, что разницы по сути, нету, а есть говнокод и не-говнокод.

Что отличия богатой и анемичной моделей являются ничем иным, как обычным качеством дизайна.

Что все, по сути, сводится к ДДД и к качеству этого самого ДДД. И все.

Спасибо, что убедил меня в том, о чем я написал вчера в этой ветке dou.ua/...​rums/topic/27753/#1619862.

Не совсем понимаю, при чем тут ФП.

В ФП IO и сайд-эффекты выносятся на самый верхний уровень чтобы доменная модель/логика была pure. Если следовать этим принципам то и rich и anemic model будут понятными и поддерживаемыми и между ними, на самом деле, разницы будет немного. Я уже писал что больше всего смучщет в anemic model в общем понимании — IO и сайд-эффекты везде. И безграничные транзакции, да.

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

Причем комбинирование, например, трех операций А, Б и В теперь выглядит так:
* Загрузка данных для А, Б и В (можно параллельно)
* Выполнение операций в нужном порядке (А, Б, В)
* Сохранение, причем порядок может быть разным (В, Б, А)

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

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

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

Что все дискуссии о рич-вс-анемик суть пустая болтология.

а они этого принять не могут :)

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

Что отличия богатой и анемичной моделей являются ничем иным, как обычным качеством дизайна.

нет, нет, суровые теоретики требуют чистоты концепций(=соответстивия действительности собственным иллюзиям)

их не смутит тотальная мультипарадигменность
когда в реляционных БД нарисовалась работа с json
когда в динамических языках появляется явно задаваемая типизация, а в императивные просачивается ФП

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

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

они как айджайл коучи

Собственно выделил суть :)

Является ли анемичным код:
OrderCancellation cancel = order.cancelUsing(additionalData);
orderDao.save(order);
sideEffectService.doOn(cancel);
?

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

которые пытаются втиснуть в ту же транзацию

И чем это плохо? Мне кажется, ты не понимаешь для чего нужны транзакции.

забывая, что в распределенных системах это не работает.

Первое, при чем тут распределенные системы? Второе, почему не работает? Предлагаю вообще не продолжать тему распределенных систем ибо мы утонем.

И чем это плохо?

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

Второе, почему не работает?

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

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

Это тот случай, когда бизнес сам себе буратина. Если хотелка бизнеса заключается в том, чтобы на 1 причинное событие повесить 100 сайд эффектов, то разработчик должен поставить бизнес перед двумя стульями — или у нас все безопасно, но очень долго, или у нас все очень быстро, но небезопасно. И это будет принципиальным решением бизнеса, которое будет реализовано архитектурно. Соответственно, реализация будет продиктована приоритетом и им же защищена от дальнейших претензий, а не аргументом типа «я у бугаенка читал анемик плохо хочу рич модель».

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

А что мешает сделать так, чтобы все связанные операции выполнялись без распределения?

Это тот случай, когда бизнес сам себе буратина.

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

А что мешает сделать так, чтобы все связанные операции выполнялись без распределения?

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

Во многих случаях eventual consistency приемлема для бизнеса.

inconsistency ты имел ввиду? Потому что странно считать приемлым факт того, что все правильно.

Некоторые сайд-эффекты могут выполняться в других серсисах

А нечего упарываться по микросервисной архитектуре :) Если действия, которые логично было бы выполнять в одной транзакции, разнесеный на разные сервисы, то я бы рассматривал это как ошибку архитектуры. Сначала повелись на микросервисный хайп, а теперь пытаемся скотчем из консервных банок сделать танк.

Т.е. ты пишешь rich model и топишь за anemic?

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

Почему ты решил, что это рич модел?

Потому что логика отмены заказа реализована в самом заказе

order.cancelUsing(additionalData)
Потому что логика отмены заказа реализована в самом заказе

Вронг.
В этом методе реализован фрагмент логики отмены заказа. Тот фрагмент, который требует только состояния самого заказа + additionalData. Но частью логики отмены заказа является и сайд эффект.
Так в чем же тут богатость?

Но частью логики отмены заказа является и сайд эффект.

Ок, тотальная транзакционность, Java Enterprise, вот это все. Убедил.

Если на самом деле интересно, почитай доки, которые я постил выше dddcommunity.org/library/vernon_2011. Там про разные трейдофы.

Я намерен бороться по возможности с анемичной доменной моделью.

Зачем? Какое валуе вы ожидаете получить?

Основным недостатком rich domain model является то что для ее построения надо глубокое понимание «домена» (bounded context). Это усложняет ее использование в «аджайл разработке», поскольку интерфейс домена может изменится в любой момент (в любом спринте :) ).
Другими словами rich domain model требует наличия фаз «оценки требований» и «дизайна», а не просто «х...-х... и в продакшен».

ри этом слой сервисов естественно остается. но каждый метод сервиса теперь маппится четко на сценарий (Use-case)

Все что пишет Дядя Боб надо фильтровать :)

> Все что пишет Дядя Боб надо фильтровать

Что именно, например?

Что именно, например?
Все

:)
Та же история с «чистой архитектурой»: с ростом количества бизнес сценариев, кодовая база становится перегруженной (особенно когда начинают дробить 1 юзкейс — 1 класс).

Ну ок. У меня другое мнение, мне кажется наоборот, бОльшая часть его идей на 90% имеет разумный смысл и не нуждается в особом критическом восприятии (упор на «особом», потому что по умолчанию любой человек и так любую информацию должен воспринимать критически).

Юзкейсы и бизнес сценарии я бы вообще не сказал, что это одна из его идей, он просто по большей части в этом повторяет Вернона AFAIK.

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

Проблема в том что без критического восприятия есть вероятность адаптировать 10%, которые не входят в 90%.
История про 100% покрытие тестами начала наберать обороты как раз дяди боба, а через пару лет форсирования этой темы, он начал рассказывать что надо понимать что 100% было про «код с бизнес вэлуе».

1) То есть SRP Вы признаете условно или вообще не признаете, я Вас правильно понимаю ?
2) В чем лично Вы видите «перегруженность» в таком подходе, объясните пожалуста ?

Пример:
Есть задача заводить и выводить деньги в/из платежной системы, вывод требует подтверждени по ссылке.
(1) Можно сделать 1 сервис с 5 методами:
— создать ввод денег
— создать вывод денег
— подствердить вывод денег
— послушать нотификацию от платежной системы (пуш)
— сходить самим на платежную систему и проверить статусы (пул)
СРП какбэ нарущается.

(2) А можно создать по отдельному классу на каждое действие, у всех этих классов будет где-то половина зависимостей одинаковые (гуйтвеи, ДАО, валидаторы и тд). Вопрос: Зачем?

Поскольку оба сценария произошли в реальности, то могу рассказать что в случае с классами люди поимели 2 проблемы:
1) Забыли, при вынесении микросервиса, перенести функциональность которая подтверждала вывод. Но тут виноват не подход, а рас...дяи разработчики.
2) Забыли добавить локи в класс который делал пул из платежной системы. То есть такая заскедуленая джоба просто портила данные, накатывая более старые значения поверх того что пришло пуш-нотификацией.

(1) Можно сделать 1 сервис с 5 методами:

Мы получим мега класс.
Со всей тучей негативных проблем — от сопровождения до тестирования.
Видел не раз такое — по 1000 и более строк. Пока человек поймет, что тут делается ...

где-то половина зависимостей одинаковые (гуйтвеи, ДАО, валидаторы и тд). Вопрос: Зачем?

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

люди поимели 2 проблемы:

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

Мы получим мега класс.
Со всей тучей негативных проблем — от сопровождения до тестирования.
Видел не раз такое — по 1000 и более строк.

Ну там было где-то раз в 5-10 раз меньше строк. Проблем с тестированием не наблюдал. Даже наоборот убодно не надо инджектить в тесто 100500 сущностей чтобы проверить сценарии типа «ввел деньги — вывел деньги».

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

То есть
— «скопировать 1 класс» — это сложнее чем «найти все (не извесное количество) классов, которые относятся к 3-5 бизнес сценариям и потом скопировать их»?
— или же «найти все БЛ-классы, которые относятся к работе с определенными сущностями и добавить туда локи» — это менен рисковано чем «добавить локи во все публичные методы одного класса»?
Сугубо ко количеству слов в описании 1 класс менее рисковано :)

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

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

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

И на выходе у нас будет
«5 классов бизнес логики + 5 валидаторов + 5 ДОА (на запись, на чтение и тд) + 3 2ФА интервейса (для создания авторизации, подтверждения и очистки состояния) + всякие аудит нотификаторы и эвент басы в которые пабликуются события»
вмето
«1 класс с бизнес логикой + 1 валидатор + 1 ДАО + 1 2ФА + всякие аудит нотификаторы и эвент басы в которые пабликуются события»

И на выходе у нас будет

Количество тех или иных сущностей зависит сугубо от бизнес требований
Если правило валидации одно для всех, то для чего 5 валидаторов — не понимаю.

Если же для каждого случая нужна своя логика, то простите 1 валидатор, который в себе собирает все Н сценариев — это очень круто, но мы опять получаем God класс

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

Если выделенные общие сущности НЕ отражают тот или иной объект или процесс реальности — они выделены неправильно.

«найти все (не извесное количество) классов, которые относятся к 3-5 бизнес сценариям и потом скопировать их»?

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

То есть ПЕРЕД бизнес операциями модицикаций мы обязательно должны подготовить некое рабочее место, а значит 1, 2 ... + блокировка

А ПОСЛЕ безусловно должны убрать за собой, а значит в том числе снять блокировку

Это общая логика, делается в рамках pre/post в едином месте. Соот. не совсем понимаю, в чем суть проблемы, почему я буду искать в рамках модуля, где сгруппированы все классы, которые реализуют соот. логику.

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

Можно сделать 1 сервис с 5 методами:

Согласен, поначалу выходит очень удобно — всё в одном классе, не надо дублировать зависимости, как Вы говорите... А потом один юзкейс меняется немножко, и ему нужна зависимость X, и второй юзкейс меняется немножко, а ему нужна зависимость Y. В итоге лет через 5 мы получаем MonsterServiceImpl, в котором с двадцатку зависимостей и с десятку не связанных между собой методов. Я это тоже говорю из опыта.

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

Между прочим, пресловутый Анклбоб сам всегда говорит: «welcome to engineering». В том смысле, что это в природе девелопмента всегда искать и идти на какие-то компромиссы. Поэтому и нельзя воспринимать тот же SRP как какую-то абсолютную величину.

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

Внимание вопрос:
Почему бы не разбивать класс __когда он начнет становится__ неудобным в поддержке?
Рефакторинг — неотемлимая часть разработки. Если не будет рефакторинга, то так же можно загадить и классы-юзкейсы.

Поэтому и нельзя воспринимать тот же SRP как какую-то абсолютную величину.

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

Почему бы не разбивать класс __когда он начнет становится__ неудобным в поддержке?

потому что людям в падло разбираться) Они видят класс в котором уже все заинжекшенно, есть какие-то похожие методы, возможно есть тесты в которых тоже уже есть все зависимости, и просто дописывают еще один метод, потом еще один, а потом мы видим класс на овер9000 строк(

Что именно, например?

Та все )
Вообще удивляет, как люди с тайтлом выше junior могут воспринимать этого клоуна всерьез.
В англоязычных комьюнити (reddit/HN/etc) он имеет уверенную репутацию профана.

А пруфы можно? Хоть несколько примеров?

Это естественное следствие распространения веб приложений. Анемичная модель очень удобна именно в вебе, в вебе теряются преимущества полноценных ОРМ (с identity map, unit of work и отслеживанием изменений) — т.к. грузить это в память непрактично и долго.

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

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

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

Ты вытягиваешь доменную модель целиком из базы (на стопитсот полей)

rich domain model и фаулеровская domain model — это не про поля, а про логику :)

Банальный кейс — надо пересчитать какое-то поле в объекте

Так создайте доменную модель для этого пересчета. Вы описали типичный антипаттерн (который довольно часто всплывает при использовании анемической доменной модели): использование одного и того же класса (например ДТО) для всех задач во всех частях приложения.

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

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

1. Да, доменная модель — это про логику. Но логика и данные в ООП — неразрывные. И даже если тебе надо вызвать один метод, то для этого надо в начале сконструировать доменный объект. А для этого надо полезть в базу и там его собрать. К сожалению, в этом плане как по мне DDD оторван от реальности — он описывает все, как будто нет хранения данных, а есть чисто логика.
2. Если заводить сто разных вариантов одних и тех же доменных объектов на все варианты то разрастается кодовая база, тобишь саппорт, расширение функционала и так далее, это повышает стоимость продукта
3. Почему в сервисе не может быть сложной логики ? Тут не понял

Но логика и данные в ООП — неразрывные.

Нет :)
Класический пример:
есть объект который умеет добавлять заказ (о.создать(цена, количество)). Никто не мешает сделать 2 реализации — одна создает и хранит заказ в себе, а вторая публикует сообщение, которое слушает первая реализация и выполняет создание локально.

ООП — это про пересылку сообщений между объектами, про протокол общения, а не про «логику держать рядом с данные» и прочую ерись о 3х китах.

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

Почитайте что такое «bounded context».
Переиспользовать одни и те же модели во всех «контекстах» — это антипаттерн, как раз потому что усложняется поддержка, ибо вы не можете быть уверены что какой-то функционал не нужен. Разные же модели как раз уменьшают стоимость, потому что изменения в одной из них не требует изменений (и тестирования) других. (Если у вас возникто желание привести пример про «переименования поля в БД», то почитайте что такое «bounded context».)

3. Почему в сервисе не может быть сложной логики ? Тут не понял

Может. Просто если она будет, то удобнее будет его реализовать как фаулеровскую доменную модель :)

Про bounded context я в курсе, он применяется если разные домены используют одно и то же понятие по разному, например понятие «оплата товара» с точки зрения покупателя — это «достань карточку с кошелька и заплати», то с точки зрения магазина — это «на счёт фирмы приходят деньги». А я про то, что с точки зрения того же поддомена могут быть разные описания одного и того же.
Когда есть разные модели для «оплата с карты visa», «оплата с карты MasterCard», «оплата с monobank» и так далее

2. Если заводить сто разных вариантов одних и тех же доменных объектов на все варианты то разрастается кодовая база, тобишь саппорт, расширение функционала и так далее, это повышает стоимость продукта

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

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

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

К сожалению, в этом плане как по мне DDD оторван от реальности — он описывает все, как будто нет хранения данных, а есть чисто логика.

Собстевенно, об этом в DDD коммюнити все и твердят. DDD — это о моделировании проблемы, а не о том, как хранить данные. И вообще NoSQL FTW.

И вообще NoSQL FTW.

Это пока тебе не нужно поддерживать целостность связей между агрегатами. Потому как в случае NoSQL это обычно реализуется через «закат солнца вручную», нетривиальными способами вроде «саги + compensating actions», со значительным повышением вероятности где-то наплужить в процессе.

Eventual consistency же. Я там вверху привел отличный документ на тему. Если eventual consistency не подходит — тогда все склеивать в один агрегат. Агрегат определяет границы транзации.

Eventual consistency же.

Я именно потому и прокомментировал, что в своё время нахлебались с ней и NoSQL без гарантий целостности.

тогда все склеивать в один агрегат. Агрегат определяет границы транзации.

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

Можно, конечно, проверять это условие чисто программно в слое бизнес-логики, но мне крайне неочевидно преимущество переписывания ручками того, что в обычной реляционной БД решается через банальный foreign key constraint.

Есть бизнес-правило "заказ всегда должен ссылаться на валидного покупателя".

Я бы для начала более пристально рассмотрел это бизнес-правило. Что значит валидный покупалель? Откуда он берется? Почему бизнес такое правило ввел. Что будет, если от него отойти? И как быть, если в бд есть невалидная запись покупателя? FK в таком случае не спасет. Вообще, я не сторонник переноса бинес-логики в БД.

мне крайне неочевидно преимущество переписывания ручками того, что в обычной реляционной БД решается через банальный foreign key constraint.

FK может лишь помочь проверить наличие записи пользователя в момент создания записи заказа.

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

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

В случае NoSQL мы добровольно от этой страховочной сетки отказываемся. И, одно дело, когда есть абсолютно чёткое понимание, зачем мы это делаем — например, система 100% будет нежизнеспособной, если не спроектировать её изначально распределённой (кстати, про распределённые транзакции с compensating actions рассказывали ещё Microsoft в 2007 году на TechEd Barcelona — своими ушами слышал).

Но из того, что я наблюдаю в жизни, гораздо чаще NoSQL притаскивают в проекты не из подобных прагматичных соображений с полным пониманием trade-offs, а из соображений моды / хайпа / строчки в резюме. И потом, наступив на грабли, начинают «закат солнца вручную»

Так же нередки случаи когда FK убирают из-за проблем с производительностью. А whitepaper про саги мой ровесник — 1987 года написания.

FK убирают из-за проблем с производительностью

Это на каких объемах, интересно? Записать в БД каждую песчинку планеты, связав ее по ФК с записью в таблице пляжей?

Это когда FK становится причиной дедлоков при большой нагрузке. Объемы могут быть необязательно большими.

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

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

Весь тред об этом.

Весь тред об этом.

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

Помоему мы здесь за подходы к ДДД.

Вопрос же не в том, когда появился whitepaper. Критика водопада была ещё чуть ли не в начале 80х, итеративно-инкрементальная модель разработки была описана примерно тогда же. Да даже функциональное программирование было известно чуть ли не с начала 90х, если не раньше.

Существенно то, когда это всё стало популяризироваться и приобрело статус «тренда» в коммерческой разработке ПО, а не когда начало обсуждаться среди седобородых computer scientists :)

Я бы для новых проектов брал NoSQL по-умолчанию. Потому что понимая как подходить к ответсвенности и границам транзаций большинство вопросов решается достаточно просто. А в исключитальных случаях есть Саги и компенсирующие действия. Я уже несколько лет работаю с MongoDB и не касаюcь SQL и это очень приятный shift.

Я бы для новых проектов брал NoSQL по-умолчанию.

вот в этом то и дело.
в «бы»

берите, кто ж вам не дает.
пишите, проектируйте как хотите — кто мешает?

Я уже несколько лет работаю с MongoDB

может потому что не осилили реляционные БД?
(это я вам ваш подход к аргументации к иному мнению демонстрирую)

выбор MongoDB для текущего проекта хоть ваш лично был?

Ты обычный троль. Мне неинтересно с тобой дискутировать ибо в конструктив не выйти. Не нужно отвечать на каждое мое сообщение.

да я и не дискутрию с вами :)

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

вот эти признаки и фиксирую :)
для других.

ну и еще типическое

в мире .NET парадигма DDD давно победила.
настолько, что если встретишь в инете статью с заголовком о чем-то DDDшном — будь уверен, примеры там будут на C#

так что и тут вопрос
а вы ли выбирали любимую парадигму, или вам ее навязало сообщество C#стов ;)

Это клиника. Похоже ты раньше тоже думал, что Rich Model — это весчь, разочаровался в ней и пришел гадить в комментариях. Но это не делаем меня адепром Rich Model. Я лишь говорю, что IO, сайдеффекты нужно выносить на верхний уроветь. Тогда и c rich и с anemic моделью будет складываться, дизайн получится более расширяемый и тестируемым. Не согласен — твое право. Зачем пытаться меня зацепить в каждом комментарии и фантазировать о моем опыте? Тебе внимания не хватает? Или признания? Или ты самоутрвердиться решил за мой счет? Может ты просто неудачник к которому никто не прислушивается и ты решил получить тут свою минуту славы? В любом случае мимо. www.youtube.com/watch?v=6N4RK9uyEQI.

ы, опять из хрестоматии, глава об упоротых
«слова апостола Павла об апостоле Петре говорят нам больше о самом Павле чем о Петре» Ницше :)

Но это не делаем меня адепром Rich Model.

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

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

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

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

для адепта — понять тождественно принять
он не врубается, что как это можно — понять и при этом остаться при прежних убеждениях.
поэтому когда ему говорят — поймите, он тут же восстает:
а почему это я должен принять, перейти на вашу сторону?
война! вы невежды, и понеслось :)

но виноваты в неконструктивности конечно другие.
адепт всегда прав конечно :)

Может ты просто неудачник к которому никто не прислушивается и ты решил получить тут свою минуту славы?

это написал тот же что и писал

приходит человек в тред, ... Его при этом называют адептом rich model, навешивают еще кучу ярлыков и говорят, что Бугаенко неправ. Что не так с этим сообществом?

или просто ники совпадают?

В любом случае мимо

это верно — сколько лет вижу в этом холиваре пишет один и тот же человек, даже одними и теми же фразами — но мимо как всегда :)

Похоже ты раньше тоже думал, что Rich Model — это весчь, разочаровался в ней

у-у-у, сколько вещей мне казались весчью...

я вообще слабо представляю как можно миновать этап юношеского максимализма

но ок, вы тот редкий человек который никогда ни в чем не разочаровывался :)

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

На самом деле, DDD может быть не только в ООП, вот хороший доклад по теме
youtu.be/Up7LcbGZFuo

На самом деле, DDD может быть не только в ООП

Интересный факт: На самом деле, в посте, на который вы ответили, нет ни слова про ДДД :)

Я не хочу так жить, надо боготься. Вот кстати хороший доклад по теме, в контексте нодовских приложений www.youtube.com/watch?v=h6rplJQTcpo

Подписаться на комментарии