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

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

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

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

Кажется, это во многом происходит из-за того, что не хватает чисто практических примеров. У людей, привыкших к геттерно-сеттерно-анемичным псевдо-mvc-архитектурам и прочитавшим книжку по DDD, часто возникают проблемы не с пониманием абстрактной концепции bounded context или aggregate root, а совсем иного рода — как сгенерировать автоинкрементный id, как отправить письмо после регистрации, что делать с зависимостями моделей (double dispatch? events?) и так далее :-)
Для согласованности в системе используются события. Отправить письмо — это инфрастуктурные вопросы.
Если следовать единому языку, то функции установщики будут очень редко использоваться.
Сама отправка — да, а вот содержимое письма вполне может определяться бизнес-логикой. ;)

Что такое «функции-установщики»? Давайте, может, англоязычными терминами оперировать? Так понятнее.
Функции установщики — setters, я имел ввиду что если используется единый язык, то анемия пропадает, очень редко будут необходимы функции сеттеры

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


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

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

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

Почему?


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

class Person
{
  string FirstName;
  string LastName;
}
В рамках DDD подхода объекты такого класса не создаются вообще. Это должна быть сущность или агрегат со своими поведенческими функциями. Ну здесь не только DDD не выполняется а и ООП инкапсуляция, думаю так

Вот только ни то (DDD), ни другое (ООП) не является обязательным условием для использования единого языка.

Поддержу.


Rich model vs Anemic Model — это деталь реализации, со своими достоинствами и недостатками.

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

Я имел ввиду не сеттеры, а double dispatch.

Хмм, а разве DDD это про реализацию? Сервисную или веб архитектуру удобно делать анемичной, чтобы не хранить состояние.

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

Всё про реализацию, на самом деле. Если не знаешь, как сделать инфраструктурную и application части, что толку от domain model? На голой теории далеко не уедешь.

Любую бизнес-логику можно описать и при помощи rich model, и при помощи anemic model.

anemic model не описывает никакой логики, это вообще тупо структура.

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

В сервисах описать, да. ООП в сервисах есть, разве что наследование не используется широко. Или, по-вашему, ООП это только когда мы пишем order.Save() вместо orderService.Save(order)?

orderService.save(order) — это выглядит как ООП.

А вот какой-нибудь order.amount = 100 — не. Анемичный сеттер — то же самое.
А вот какой-нибудь order.amount = 100 — не [ООП]

Почему нет-то?

Потому что ООП — это когда this.amount = 100
order = this.orders.last();
order.amount = 100;
Имеется ввиду то, что это в aggregate root, да?

В принципе, вполне допустимо. Но вообще сведение всей логики в aggregate root и использование некорневых моделей чисто как anemic/persistence models — это, на мой взгляд, такой DDD-шный антипаттерн, последствие анемичного мышления с сервисами. Перекинули код из сервиса в AR и все.

Прекрасный аргумент, да.


Вот у меня есть банальная совершенно ситуация, строчки заказа, состоящие из {товар, количество} (единицы измерения не учитываем, везде штуки). Пользователь количество вводит руками в UI. Почему проставить его в соответствующее свойство соответствующего объекта — это не ООП?

http://www.martinfowler.com/bliki/AnemicDomainModel.html

Там нет ответа на этот вопрос. Более того, за свойством Amount вполне может быть поведение.

Давайте обойдемся без рассматривания языков, где есть костыли типа property setters (тем более, что от этого лучше не становится).

Ответ там есть:
The fundamental horror of this anti-pattern is that it's so contrary to the basic idea of object-oriented design; which is to combine data and process together.

order.amount = 100 — это чистая процедурщина со структурами, никакого бизнес-действия за этим нет. order.setAmount(100) — то же самое.

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

>Пользователь количество вводит руками в UI. Почему проставить его в соответствующее свойство соответствующего объекта — это не ООП?

Потому что мыслить надо бизнес-действиями, а не полями, структурами и таблицами в базе данных. За любым вводом в UI скрывается какое-то действие.
order.amount = 100 — это чистая процедурщина со структурами, никакого бизнес-действия за этим нет.

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


Вот у меня есть банальная совершенно ситуация, строчки заказа, состоящие из {товар, количество} (единицы измерения не учитываем, везде штуки). Пользователь количество вводит руками в UI.

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

Причина называется "покупатель сказал, сколько хочу".

Строка типа order.amount = 100 ничего не говорит нам о том Rich Model это или Anemic, а может DTO, ООП это или не ООП. Она вполне может находится в одном из методов order, она может находить в фабрике или конструкторе, она может являться частью публичного интерфейса сущности Rich Model, а может внутреннего.

Воувоу, сумма заказа передается в конструкторе? Сумма заказа обычно рассчитывается, причем по довольно сложным правилам. И у нее есть определенные моменты пересчета (и совсем не обязательно, чтобы это был момент установки какого-то свойства).


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

Вполне себе ООП и даже вполне может быть по DDD. Если в домене есть операция установки суммы ордера, то она вполне может выглядеть так. Особенно если эта операция происходит в корневом агрегате.
НЛО прилетело и опубликовало эту надпись здесь
Спасибо за дельное замечание. Картинки поменяли. Пример взят из книги, а картинки свои… уже.
Продажа книги реализаторам и/или непосредственно покупателям
Поставка экземпляров книги реализаторам или покупателям


Правильный термин — дистрибьютор.
Не проработали единый язык).
Cпасибо за замечание. Примеры взяты с книги, термины эти тоже оттуда.
Не могли бы Вы прояснить свою мысль, или пояснить подробнее:
Такие диаграммы носят неформальный характер, поэтому нет смысла использовать формальное моделирование (как UML).

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

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


Т.е формально, декларируется: давайте изобретем свою семантику и нотацию для нашего проекта, потому что…
Тот же UML не запрещает расширять свою семантику и нотацию, если ее не хватает. (Хотя ее часто не хватает, для специфических вещей :) )
Возникает вопрос: Зачем изобретать свою семантику и нотацию, если стандартные средства и инструменты не запрещают и позволяют расширять существующие общеизвестные?

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

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

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

Я не говорю, сейчас о артефактах, внутри проектов.

На самом деле, "единый язык" просто никак не связан с нотацией. Можно использовать единый язык и использовать UML, можно использовать единый язык и не использовать стандартные нотации. Это разные уровни семантики.

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

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

Единый язык — это глоссарий и конкретные сущности проекта. По этому вопросу нет.
вопросы по:
Такие диаграммы носят неформальный характер, поэтому нет смысла использовать формальное моделирование (как UML).

Народ лет 10...25 изобретал UML, BPMN, IDEFx и т.д. все они в той или иной степень детализации могут нарисовать что угодно (вопрос цены работы и инструмента)
Чем этот «смысл» измеряется?
Нужно исбегать формальных способов описания единого языка, не только UML. Это сковывает и разработчиков и экспертов. Построение единого языка — процесс более творческий, чем формальный.

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

Это вроде как: давайте этот проект сделаем на китайском, это на хинди, этот клинописью, этот узелковым письмом, а этот вообще на языке запахов и цвета. Но никоем образом не будем делать все проекты на русском или английском языке.
Народ лет 10...25 изобретал UML, BPMN, IDEFx и т.д. все они в той или иной степень детализации могут нарисовать что угодно (вопрос цены работы и инструмента)

"Смысл" измеряется стоимостью усилий по использованию инструмента (стоимость инструмента + стоимость специалиста) в сравнении со стоимостью выигрыша от использования этого инструмента.


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


А если учесть что «результат» должен быть прочитан третьей стороной «без усилий»,

Как раз наоборот. Если вы используете UML, вам надо, чтобы ваша третья сторона умела читать UML. Если вы используете примитивную нотацию, вам достаточно объяснить эту нотацию.

Вообще-то без разницы что учить третьей стороне: самопальную нотацию или «стандартизированную». Были прецеденты.
То же UML говорит: вы можете нарисовать что угодно и как угодно, только задекларируйте свою нотацию. Вроде как для XML: задекларируйте схему документа, если не хотите проблем.

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

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

Разница в объемах.


Можно где-то глянуть примеры «избыточности стандартной нотации» и «разумной достаточности» для сравнения?

Для описания доменной модели достаточно (в рамках одного bounded context) ровного одного примитива (доменная сущность) и двух типов связи (ссылка и композиция), причем для ссылки нужно знать направление и семантику связи. Все.


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

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

По идее это конечная цель.
Если упоминается какая-то сущность А в связи с сущность Б, при этом описана только сущность А, без Б то зачем такой результат?
Если описана сущность В без атрибутов, с припиской «должна быть корректна» а чему корретна? ХЗ. Нафига такой результат?
Если указывается бизнес процесс без начала и/или конца или одной ветки логики, и/или участвующих сущностей, участников — то зачем такая работа?
Это просто «поток сознания» не имющий практической ценности.
Если надо тратить ресурсв на формализации и верификации, то обычно овчинка выделки не стоит, слишком часто эта часть документации меняется.

Для этого есть специфическое ПО.

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

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

Единый язык — это не результат работы, он даже может не быть частью даже рабочей документации в явном виде. Даже явный глоссарий имеет вспомогательную роль, а уж диаграммы его поясняющие можно от руки рисовать — они не описывают полностью сущности или процессы, они служат средством подтверждения того, что у всех членов команды единое понимание языка. Например, что клиент — это человек, совершивший покупку. На данном этапе не важно, например, может ли он совершить много покупок, просто фиксируется связь типа «совершил» между сущностями «клиент» и «покупка». Есть связь — клиент, нет — просто человек. Формальные инструменты заставят вас детально описывать сущности и связь, хотя сейчас вам это не важно, вам нужно только понять какие сущности вообще есть и какие между ними связи. Хотя бы чтобы понятно всей команде написать имена этих сущностей на формальной диаграмме, когда решите её создавать. Если вы показываете формальную верифицированную диаграмму, а первый вопрос у хотя бы одного члена команды: «а Customer — это что?» — значит ві слишком рано начали использовать формальніе инструменті.
Если вы показываете формальную верифицированную диаграмму, а первый вопрос у хотя бы одного члена команды: «а Customer — это что?» — значит ві слишком рано начали использовать формальніе инструменті.

Вполне закономерый вопрос — а кто-что такое Customer?
Если нету в глоссарии, нету в диаграммах, если не понятен его жизненный цикл, не понятен его функционал — зачем это техническому спецу (программисту разработчику архитектору, qa)?
Это получается «сферический конь в вакууме». Traceability failed. фиксить.

Нужно тратить ресурсы на обучение всех членов команды формальному языку,

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

значит ві слишком рано начали использовать формальніе инструменті

В какой-то момент у вас в руках, например, бумажный билетик, на котором написанно 10 денег. И в какой-то момент возникает вопрос: а эти «10 денег» это что ?! Откуда оно? и как выглядит? как Amount + Currency или String и как это отобразить на «физику»?
Это провоцирует вопрос: А когда нормально или поздно для «формальніе инструменті»? Когда подписан бюджет проекта? когда будете пытаться генерить скрипты sql для базы или wsdl схемы? когда QA будет пытаться сделать User Accepted Tests?

И как вы будете перетягивать из «нефомальных тулзов» в «формальные тулзы» если неформальные заняли 340 часов и за чей бюджет?
Если нету в глоссарии, нету в диаграммах, если не понятен его жизненный цикл, не понятен его функционал — зачем это техническому спецу (программисту разработчику архитектору, qa)?

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

Не придётся. Грубо, они привлекаются, чтобы точно и однозначно составить ТЗ, а если где-то какие-то умолчания и допущения, то они должны быть расшарены между всеми членами команды.
И как вы будете перетягивать из «нефомальных тулзов» в «формальные тулзы» если неформальные заняли 340 часов и за чей бюджет?

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

Это слишком оптимистично сказано :) Сомневаюсь, что найдется Клиент, который будет готов оплатить изучение всей командой всей нормативной базы, на основе которой строится его бизнес. Даже если опустить вопрос денег, встает вопрос времени на изучение. Неделю — могут позволить на «познакомиться с выжимкой», но не 6...12 месяцев!
Полагаю «Понимать в рамках, необходимых для решения бизнес- проблемы Клиента» будет корректнее.

Это бизнес аналитика, и кусок ALC.
И она как раз формализуется и верифицируется при корректном подходе и инструментарии и своя нотация ко всему этому есть. (в нескольких вариантах)
Если вы хотите получить конкрентное пояснение «откуда это» или «а кто это сделал и почему» — вам придется это делать. В противном случае, на более-менее серьезном проекте, вам зададут вопрос или юристы или фин директор или работники — а за чей счет банкет? или клиент задаст вопрос — что за фигню вы мне сваяли и если не сможете внятно ответить, еще и неустойку по контракту заплатите (плюс репутационные риски).
Для мелких «сервисных» проектов ~ 10 тыс часов, когда все носители знания под рукой и без поддержки пару лет — такие объяснения еще могут прокатить. Для больших — сомнительно.
Для продуктовых проектов — тоже сомнительно.

Я возможно что-то упускаю из виду. Скажите, в этой статье есть что-то, чего нет в The Big Blue Book Эванса или в той же книге Вернона, на которую вы сами ссылаетесь? Или в материалах, перечисленных в этой статье https://habrahabr.ru/post/61524/ семилетней давности?


Сначала обрадовался заголовку в ленте. Ожидал разбора классных кейсов в нестандартных доменах. А ощущение, что почитал ещё ужатый InfoQ: DDD Quickly.

В семилетней статье нету, например, описания что такое служебная подобласть (Supporting Subdomain).
Этого нету и в DDD Quickly. Пример карты контекстов выбран не с этих книг.

В семилетней статье нету, например, описания что такое служебная подобласть (Supporting Subdomain). Этого нету и в DDD Quickly.

В вашем случае служебная подобласть не тоже самое, что GENERIC SUBDOMAINS? О которых рассказывается и в оригинальной книге Эванса и упоминается в InfoQ (что логично, учитывая что это просто краткое содержание оригинала)? Из вашего описания, я не увидел между ними никакой разницы, если объясните, буду признателен.

Не то что в ходит в CORE DOMAIN.
НЛО прилетело и опубликовало эту надпись здесь
Предметная область (Domain) – это то, что делает организация, и среда, в которой она это делает.

Это шутка, я надеюсь?
Даже в небольшом производственном предприятии имеются минимум несколько «предметных областей» (снабжение, сбыт, производство, финансы, кадры, менеджмент), оказывающихся, даже при поверхностном рассмотрении, контекстами и областями знаний/деятельности. Предметная область — это бухгалтерский учет, логистика или металлообработка. Программирование, кстати, тоже.
К сожалению, не могу оправдать Ваши надежды. Если мы говорим о, например, разработке ERP, то данное утверждение является правильным. Конечно, если мы говорим о разработке небольшого программного продукта, типа системы бухгалтерского учета или учета рабочего времени программистов, то тогда предметной областью этого продукта является то, что Вы назвали.
Я с Вами согласен, что текст в этом месте стоит уточнить. Благодарю за комментарий.

Скорее это разные контексты предметной области типа производства метизделий.

Медсестры назначают вакцины от гриппа в стандартных дозах

а кто об этом должен знать?

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

если вакцина в дозах, а управляют этим медсестры, то 3й вариант

а если совсем абстрактно, то

administerVaccine :: Nurse -> VaccinaType -> Dose -> IO ()

или

administerVaccine :: Nurse -> Patient -> VaccinaType -> Dose -> Patient

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

Vaccination(VaccineId, PatientId, NurseId)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации