Вы находитесь на странице: 1из 9

Введение

По окончанию данного мастер-класса у вас будет краткое представление о DDD и


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

Предметная область (Domain)


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

DDD
Domain Driven Design (DDD) — это подход, который позволяет нам преуспеть в
понимании и построении моделей программных продуктов. Он предоставляет нам
инструменты стратегического и тактического моделирования для разработки
высококачественного программного обеспечения, которое соответствует нашим бизнес-
целям.

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

Domain Driven Design (DDD) — эффективный подход к разработке приложений в сложных


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

Что же такое DDD

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

Основной целью применения DDD является получение высококачественной модели


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

Единый язык (Ubiquitous Language)


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

Этот коллективный язык терминов называется — единый язык. (Ubiquitous Language). Это
один из основных и самых важных шаблонов DDD. Это не бизнес жаргон, навязанный
разработчикам, а настоящий язык, созданный целостной командой – экспертами в
предметной области, разработчиками, бизнес-аналитиками и всеми, кто вовлечен в
создание системы. Роль в команде не столь существенна, поскольку каждый член
команды использует для описания проекта единый язык.

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


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

Разработка для проекта Единого языка (ubiquitous language) обеспечивает эффективные


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

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


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

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

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


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

Так у программистов появилось абстрактное понятие «продаваемое место»,


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

Заметили как сложно воспринимается предыдущий абзац?

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


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

Ограниченный контекст (Bounded Context)


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

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

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

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

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

Ограниченный Контекст (Bounded Context) — ключевой инструмент DDD, это явная


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

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

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


команд(ы), что может значительно ускорить работу по проекту.

Зачем нужны контексты?

Проблема №1

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


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

Проблема №2

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

Проблема №3

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

В качестве бонуса

Bounded Context — это реализация принципа слабой связанности на более высоком


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

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

Value Object (Объект-значение)


Объект-Значение (Value Object) — это объект, который представляет собой понятие из
предметной области. В DDD (Domain Driven Development — разработка на основе
предметной области, или предметно-ориентированное программирование) важно то, что
Value Object поддерживает и обогащает Единый Язык вашей Предметной Области.
Это не только примитивы, которые представляют собой некоторые значения, — они
являются полноправными гражданами Предметной Области, которые формируют
поведение вашего приложения.
Хорошие примеры Value Object-ов, упомянутые у Мартина, — деньги и время. При
создании ГИС-приложений вы можете прийти к такому Объекту-Значению, как
Location($lat, $long), который будет инкапсулировать широту/долготу и подобное. Вопрос,
который вы, вероятно, захотите задать — почему это лучше, чем просто передать два
float`а в массиве и называть это $location?

Преимущества использования Value Objects

Самое главное заключается в том, что эти объекты отражают язык, на котором вы
разговариваете с другими разработчиками — когда вы говорите «Место»(Location) все
знают, что это значит. Второе преимущество заключается в том, что Value Object может
валидировать значение — подходит оно или нет для того, чтобы создать такой объект.
Третьим преимуществом является то, что вы можете полагаться на тип — вы знаете,
что если такой Value Object был принят в качестве аргумента, он будет всегда в
допустимом состоянии и вам не нужно беспокоиться об этом. И также Value Object может
содержать некоторые специализированные методы, которые имеют смысл только в
контексте этого значения и могут быть расположены в этом объекте (не нужно создавать
странные классы-утилиты).

Пример Value Object

В качестве примера Value Object-а, который является распространённым для всех веб-
приложений, я создал EmailAddress:
Вышеприведённая реализация:

· обеспечивает, что Объект-Значение EmailAddress всегда находится в допустимом


состоянии;
· позволяет использовать подсказки типов (тайп-хинтинг) и убрать проверки email-ов
(впоследствии упростить логику приложения);
· предоставляет возможность приведения к строке;
· предоставляет метод для сравнения его с другими EmailAddress.

Одержимость примитивами

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


примитивных значений, но подобные вещи описаны как "код с запашком" или так же
извесны под названием «Одержимость примитивами»:
Одержимость примитивами — это использование примитивных типов данных для
представления сущностей Предметной области. Например, мы используем String для
представления сообщения, Integer в качестве суммы денег, или Struct/Dictionary/Hash для
представления конкретного объекта.
Использование Value Object-ов является одной из стратегий борьбы с этим запахом.
Ключевая идея здесь — это собрать поведение данных вокруг своего объекта. В
противном случае такие действия будут разбросаны по всему коду, что может привести к
ненужной сложности и заставит вас относиться с опаской к значениям, переданным в
методы.
Избыточное использование примитивов и/или нефункциональных Value Object`ов еще
известно как шаблон Anemic Model, но вернемся к этому позже...

Неизменяемость

Очень важно помнить и понимать, что Value Object является неизменяемым. Почему это
такое важное понятие? Задумайтесь о реальности вокруг вас и какие значения вы
используете — число один, красный цвет и так далее. Вы не можете изменить эти
значения — это не имеет смысла, менять красный цвет на зеленый, и продолжать
называть его красным — это больше не красный, и называние зеленого красным будет
путать людей. Таким же образом Value Object в вашем приложении должны быть
неизменными.
Например, у вас есть сущность «Собрание» в вашей Предметной Области, и у этого
Собрания есть некоторая Дата (Value Object). Теперь дата встречи изменилась, но не
сама дата - 22 марта все еще 22 марта. Это собрание, которое требует назначения новой
даты, а не изменения самой даты, поэтому выкиньте старую дату и создайте новую.

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

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

Aggregate (Агрегат)
Агрегатом называется кластер из объектов сущностей или значений. То есть эти
объекты рассматриваются как единое целое с точки зрения изменения данных. У каждого
агрегата есть корень Aggregate Root и граница, внутри которой всегда должны быть
удовлетворены инварианты. Также имеют свойство меняться в рамках одной транзакции
по бизнес-процессу;

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


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

Агрегат — набор объектов предметной области, которые сохраняются в источнике


данных (например, реляционной БД), используются совместно в логике приложения,
часто имеют связи на уровне базы данных
Repository (Репозиторий)
Репозиторий — область памяти, которая предназначена для безопасного хранения
помещенных в нее элементов. Именно этим является Репозиторий в DDD. Репозиторий
используется для агрегатов. Помещая агрегат в соответствующий Репозиторий , а
затем извлекая его оттуда, вы получаете целостный объект. Если агрегат будет изменен,
то изменения будут сохранены. Если агрегат будет удален, то его уже нельзя будет
извлечь.

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


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

Есть два типа Repository:

1. Ориентированные на имитацию коллекций;


2. Ориентированные на механизм постоянного хранения.

Domain Service (Сервис домена)


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

· Операция, выполняемая службой, относится к концепции предметной области,


которая не принадлежит ни одной из существующих сущностей;
· Операция выполняется над различными объектами модели предметной области;
· Операция не имеет состояния.

Domain Event (Событие домена)


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

Module (Модуль)
Модули внутри модели являются именованными контейнерами для некоторой группы
объектов предметной области, тесно связанных друг с другом. Их цель – ослабление
связей между классами, которые находятся в различных модулях. Так как модули в
подходе DDD – это неформальные или обобщенные разделы, их следует правильно
называть. Выбор их имен является функцией единого языка.
Factory (Фабрика)
Некоторые агрегаты или сущности могут быть достаточно сложными. Сложный объект
не может создавать сам себя посредством конструктора. (В книге Эрика Эванса был
приведен пример: двигатель автомобиля, который собирается либо механиком, либо
роботом, но он никак не должен собираться сам по себе.) Еще хуже, когда передают
создание сложного объекта на клиент. Так, клиент должен знать о внутренней структуре и
взаимосвязях внутри объекта. Это нарушает инкапсуляцию и привязывает клиента к
определенной реализации (таким образом, при изменении объекта придется менять и
реализацию клиента).

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

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

Domain Model vs Anemic Domain Model


Мартин Фаулер (Martin Fowler), еще одна видная фигура в DDD-тусовке описывает
слабую модель предметной области следующим образом.
Основным признаком слабой модели предметной области является то, что на первый
взгляд она выглядит как что-то реальное. Там есть объекты, многие из которых названы
по именам в пространстве предметной области, и эти объекты связаны широкими
отношениями и структурой, которую имеют настоящие модели предметной области.
Замкнутый круг возникает, когда вы посмотрите на поведение и поймете, что это вряд ли
поведение в этих объектах, делающее их чем-то большим, чем контейнеры методов
получения и задания.

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

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

Как бы там ни было, если вы имеете очень простой ограниченный контекст или
микрослужбу (службу CRUD), слабая модель предметной области в форме объектов
сущности только со свойствами данных может быть достаточно хороша, и, возможно, не
имеет смысла реализовывать более сложные шаблоны DDD. В таком случае это будет
просто модель сохраняемости, так как вы намеренно создали сущность только с данными
для целей CRUD.

Именно поэтому архитектуры микрослужб прекрасно сочетаются с мультиархитектурным


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

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


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

................

Если строго следовать правилам DDD то нельзя в домене


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

Что лучше для DDD, Laravel или Symphony?

Идеальная структура DDD приложения. Не стоит воевать с


фреймворком

Модульный монолит

Вам также может понравиться