Что такое ddd java
Проектируем по DDD. Часть 1: Domain & Application
xxx: пока скачаешь одну библиотеку, пока другую, пока их xml конфигом на полметра склеишь, пока маппинг для hibernate настроишь, пока базу нарисуешь, пока веб-сервисы поднимешь
xxx: вроде и hello world пишешь, а уже две недели прошло и всем кажется, что это учетная система для малого бизнеса
ibash.org.ru
В серии из нескольких статей я хотел бы на простом, но имеющим некоторые нюансы, примере рассказать о том, как имея готовые domain и application слои реализовать под них инфраструктуру для хранения и извлечения данных (infrastructure for persistence) используя две различные популярные технологии – Entity Framework Code First и Fluent NHibernate. Если вы хотя бы слышали про три буквы DDD и у вас нет желания послать меня на тоже три, но другие буквы — прошу под кат.
Собственно, само название DDD (Domain-driven development) говорит о том, что разработка начинается и «ведется» предметной областью, а не инфраструктурой и чем-либо ещё. В идеальной ситуации при проектировании вы не должны задумываться о том, какой фрэймворк для хранения данных будет использоваться и как-то менять домен под его возможности – так поступим и мы: в первой статье я опишу пример, под который собственно мы и будем реализовывать инфраструктуру.
Domain Layer
☕ Пример проекта Java Backend: DDD, микросервисы, Spring Cloud и AWS (Часть 3)
Bakhtiyor Ruzimatov
Предметно-ориентированное проектирование
Основные определения
DDD – это набор правил, которые позволяют принимать правильные проектные решения. Такой подход позволяет значительно ускорить процесс разработки программного обеспечения в незнакомой области, а также с легкостью развивать и сопровождать его в будущем.
DDD – это борьба со сложностью бизнес-логики.
— архитектурной части (стратегическое моделирование):
— реализационной части (тактическое моделирование):
В чем преимущество DDD?
Начните со стратегического моделирования:
Затем, переходите к тактическому моделированию:
Давайте проясним основные понятия архитектурной и реализационной частей.
Архитектурная часть
Знание предметной области (Domain Knowledge)
Представьте, вы начали новый проект (это может быть сфера медицины, страхования или финансов) и вам необходимо собрать как можно больше информации по предметной области. Вы начинаете ее изучать и исходя из требований проекта исследуете определённую область знаний. Общение со специалистами со стороны заказчика – это необходимость в проектной разработке.
Единый язык (The Ubiquitous Language)
В процессе работы вы должны договориться с заказчиком на каком языке вы будете общаться, вам придется создавать единый словарь терминов и определений, которые понадобятся при проектировании архитектуры и реализации проекта. В жизненном цикле проекта словарный запас этого языка будет постоянно пополняться и расширяться из знаний предметной области.
Предметная область (Problem Space)
Определение предметной области является начальным этапом предметно-ориентированного проектирования – это главная бизнес-задача. В зависимости от сложности проекта, предметных областей может быть от одной и более.
Поддомены (SubDomain), ограниченные контексты (Bounded Context)
Реализационная часть
Модель предметной области (Domain Model)
В процессе создания решения определяется модель предметной области, она и является самой важной частью бизнес-логики ограниченного контекста. На языке бизнеса определяются следующие понятия (в сопоставлении с DDD определениями):
Далее, в процессе создания модели предметной области примерного проекта эти понятия будут раскрыты и реализованы.
Пользовательские истории (User Experience, UX)
Основные определения
Сценарий действий, как это будет происходить
Первичная регистрация пациента:
Конечно, со стороны заказчика может быть предложен расширенный вариант UX. В нашем вымышленном проекте достаточно и этой информации.
Строим архитектуру проекта
Определяем основную предметную область – это дистанционная экстренная медицинская помощь пациенту.
Определяем артефакты основной предметной области – у нас будут следующие артефакты:
Поддомены (ограниченные контексты)
Мы должны определить четкое разделение основной предметной области на различные бизнес-области, независимые и со своим бизнес-языком.
Разделяем основную предметную область на поддомены
Определяем несколько ограниченных контекстов.
В нашем случае, контексты, это:
Эта область включает обращение пациента в службу экстренной помощи, а также следующие операции:
Эта область определяет состояние здоровья пациента и выявляет степень экстренности оказания медицинской помощи, а также следующие операции:
Эта область включает в себя процесс лечения пациента, а также следующие операции:
Эта область включает в себя процесс оказания экстренной помощи пациенту, а также следующие операции:
Давайте установим правила проектирования для нашего приложения.
в отношении ограниченных контекстов:
для связи микросервисов:
В проекте мы имеем четыре основные бизнес-области и предполагаем сопоставлять одну бизнес-область к одному ограниченному контексту.
Бизнес-область | Ограниченный контекст |
вызов (recourse) | Вызов (recourse) |
диагностика (diagnostics) | Диагноз (diagnose) |
лечение (treatment) | Лечение (treatment) |
отслеживание (tracking) | Отслеживание (tracking) |
Определим модель предметной области
У нас будет два основных набора артефактов для модели предметной области.
— для модели основной предметной области:
— для операций модели предметной области:
Давайте, разберемся со всеми по порядку
Агрегаты (Aggregates) в ограниченном контексте
Агрегат – это единый элемент во время любых операций, которые обновляют состояние данного агрегата. Агрегаты отвечают за сохранение всех состояний и бизнес-правил, а также устанавливают область видимости ограниченного контекста. Каждый агрегат определяет область логической целостности ограниченного контекста и содержит в себе:
Каждый агрегат должен быть обозначен идентификатором агрегата (Aggregate Identifier) и реализуется при помощи бизнес-ключа (Business Key).
Каждая сущность в ограниченном контексте должна иметь свою идентичность, они привязаны к агрегату и не могут существовать без агрегатов.
Определяем сущности в наших агрегатах.
Они не имеют собственной идентичности и их можно заменить в любом экземпляре агрегата.
Это операции, которые требуют изменения состояния в ограниченном контексте.
Это операции, которые запрашивают состояние ограниченного контекста.
сообщают об изменении состояния ограниченного контекста.
Команды, запросы, события
В распределенных системах на основе микросервисов реализуется механизм поддержания логической целостности данных при взаимодействии нескольких микросервисов. Саги реализуют это методом хореографии (Event Choreography) или оркестровки событий (Event Orchestration). Сага позволяет приложению поддерживать согласованность данных между сервисами без использования распределенных транзакций.
Определяем сервисы модели предметной области
Введем следующие определения. Ограниченный контекст:
Существует три типа сервисов модели предметной области для ограниченного контекста:
Резюме
Мы вкратце прошлись по DDD применительно к вымышленному проекту экстренной медицинской помощи. Были даны определения для ограниченных контекстов, агрегатов, модели предметной области и других понятий, необходимых при предметно-ориентированном проектировании.
Domain Driven Design на практике
Эванс написал хорошую книжку с хорошими идеями. Но этим идеям не хватает методологической основы. Опытным разработчикам и архитекторам на интуитивном уровне понятно, что надо быть как можно ближе к предметной области заказчика, что с заказчиком надо разговаривать. Но не понятно как оценить проект на соответствие Ubiquitous Language и реального языка заказчика? Как понять, что домен разделен на Bounded Context правильно? Как вообще определить используется DDD в проекте или нет?
Последний пункт особенно актуален. На одном из своих выступлений Грег Янг попросил поднять руки тех, кто практиукует DDD. А потом попросил опустить тех, кто создает классы с набором публичных геттеров и сеттеров, располагает логику в «сервисах» и «хелперах» и называет это DDD. По залу прошел смешок:)
Как же правильно структурировать бизнес-логику в DDD-стиле? Где хранить «поведение»: в сервисах, сущностях, extension-методах или везде по чуть-чуть? В статье я расскажу о том, как проектирую предметную область и какими правилами пользуюсь.
Все люди лгут
Не специально конечно:) Дело в том, что бизнес-приложения создаются для широкого спектра задач и удовлетоврения интересов различных групп пользователей. Бизнес-процессы от начала до конца в лучшем случае понимает только топ-менеджмент. Не редко понимает неверно, кстати. Внутри подразделенеий пользователи видят только некоторую часть. Поэтому результатом интервьюирования всех заинтересованных сторон обычно становится клубок противоречий. Из этого правила вытекает следующее.
Сначала аналитика, потом проектирование и лишь затем — разработка
Начинать нужно не со структуры БД или набора классов, а с бизнес-процессов. Мы используем BPMN и UML Activity в сочетнии с контрольными примерами. Диаграммы хорошо читаются даже теми, кто не знаком со стандартами. Контрольные примеры в табличной форме помогают лучше обозначить пограничные кейсы и устранить противоречия.
Абстрактные разговоры — просто потеря времени. Люди убеждены, что детали не значительны и «незачем вообще их обсуждать, ведь все уже ясно». Просьба заполнить таблицу контрольных примеров наглядно показывает, что вариантов на самом деле не 3 а 26 (это не преувеличение, а результат аналитики на одном из наших проектов).
Таблицы и диаграммы — основной инструмент коммуникации между бизнесом, аналитикой и разработкой. Параллельно составлению BPMN — диаграмм и таблиц контрольных примеров начинаем записывать термины в тезаурус проекта. Словарь поможет позже для проектирования сущностей.
Выделяем контексты
Единую предметную модель для всего приложения можно создать только в случае, когда на уровне топ-менеджмента принята и реализована политика использования единого непротиворечивого языка в рамках всей организации. Т.е. когда отдел продаж говорит производству «аккаунт», они оба понимают слово одинаково. Это один и тот же аккаунт, а не «аккаунт в CRM» и «юр.лицо клиента».
В реальной жизни я такого не видел. Поэтому желательно сразу грубо «нарезать» предметную модель на несколько частей. Чем меньше они связаны, тем лучше. Обычно все-таки получается нащупать некоторый набор общих терминов. Я называю это ядром предметной области. Любой контекст может зависеть от ядра. При этом крайне желательно избегать зависимостей между контекстами. Потенциально такой подход приводит к «распуханию» ядра, однако взаимная зависимость контекстов порождает сильную связность, что хуже «толстого» ядра.
Архитектура
Структура проекта
.NET-проекты я структурирую следущим образом:
Моделируем сущности
Под сущностью будем понимать объект предметной области, обладающий уникальным идентификатором. Для примера возьмем класс, описывающий российскую компанию, в контексте получения аккредитации в неком ведомстве.
Чтобы правильно выбрать агрегаты и отношения зачастую одной итерации недостаточно. Сначала я накидываю основную структуру классов, определяю отношения один к одному, один ко многим и многие ко многим и описываю структуру данных. Затем трассирую структуру по бизнес процессам, сверяясь с BMPN и контрольными примерами. Если какой-то кейс не укладывается в структуру, значит при проектировании допущена ошибка и структуру необходимо изменить. Результирующую структуру можно оформить в виде диаграммы и дополнительно согласовать с экспертами в предметной области.
Эксперты могут указать на ошибки и неточности проектирования. Иногда в процессе выясняется, что для некоторых сущностей нет подходящего термина. Тогда я предлагаю варианты и через некоторое время находится подходящий. Новый термин вносится в тезаурус. Очень важно обсуждать и договариваться о терминологии совместно. Это исключает большой пласт проблем непонимания в будущем.
Выбор уникального идентификатора
Настоящие конструкторы
Для материализации объектов ORM чаще всего используют reflection. EF сможет дотянуться до protected-конструктора, а программисты – нет. Им придется создать корректное юр. лицо, идентифицируемое по ИНН и КПП. Конструктор снабжен гардами. Создать не корректный объект просто не получится. Extension-метод ValidateProperties вызывает валидацию по DataAnnotation — атрибутам, а NullIfEmpty не дает передать пустые строки.
Для валидации ИНН специально написан атрибут следующего вида:
Конструктора без параметров объявлен защищенным, чтобы его использовала только для ORM. Для материализации используется reflection, поэтому модификатор доступа — не помеха. В «настоящий» конструктор переданы оба необходимых поля: ИНН и КПП. Остальные поля юр.лица в контексте системы не обязательные и заполняются представителем компании позже.
Инкапсуляция и валидация
Можно еще больше усилить систему типов. Однако в описанном по ссылке подходе есть существенный недостаток: отсутствие поддержки со стороны стандартной инфраструктуры ASP.NET. Поддержку можно дописать, но такой инфраструктурный код чего-то стоит и его нужно сопровождать.
Свойства для чтения, специализированные методы для изменения
Альтернативный вариант — использовать паттерн «состояние» и вынести поведение в отдельные классы.
Спецификации
Некоторое время было не ясно, что лучше писать extension’ы модифицирующие Queryable или возиться с деревьями выражений. В конечном итоге, реализация LinqSpecs оказалась самой удобной.
Extension-методы
Ad hoc полиморфизм для интерфейсов (чтобы не приходилось реализовывать методы в каждом наследнике) рано или поздно появится в C#. Пока приходится довольствоваться extension-методами.
Extension-методы подходят для использования в LINQ для большей выразительности. Однако, методы ByInnAndKpp и ByInn нельзя использовать внутри других выражений. Их не сможет разобрать провайдер. Более подробно про использование extension-методов а-ля DSL рассказал Дино Эспозито на одном из DotNext.
Вопрос эквивалентности вариантов с Select и SelectMany с точки зрения IQueryProvider я еще до конца не изучил. Буду благодарен любой информации на эту тему в комментариях.
Связанные коллекции
Желательно использовать только в блоке Select для преобразования в SQL-запрос, потому что код вида company.Documents.Where(…).ToList() не построит запрос к БД, а сначала поднимет в оперативную память все связанные сущности, а потому применит Where к выборке в памяти. Таким образом, наличие коллекций в модели может крайней негативно отразиться на производительности приложения. При этом рефакторинг будет произвести сложно, потому что придется передавать необходимые IQueryable из вне. Чтобы контролировать качество запросов нужно поглядывать в miniProfiler.
Сервисы (Service)
Менеджеры (Manager)
TPT для union-type
Иногда одна сщуность может быть связана с одной из нескольких других. Для создания непротиворечивой системы хранения можно использовать TPT, а для control flow — pattern matching. Этот подход подробно описан в отдельной статье.
Queryable Extensions для проекций в DTO
CQRS для отдельных подсистем
При работе в условиях высокой неопределенности риск ошибки проектирования также велик. Прежде чем проектировать структуру БД, принимать решения о денормализации или писать хранимые процедуры есть смысл прибегнуть к быстрому макетированию и проверить гипотезы. Когда есть уверенность: что на входе, а что на выходе можно заняться оптимизацией.
Подход ограничено-применим для очень-очень нагруженных ресурсов из-за накладных расходов на IOC-контейнер и memory traffic для per request lifestyle. Однако, все IQuery можно сделать singleton’ами, если не инжектировать зависимости от БД в конструктор, а вместо этого использовать конструкцию using.
Работа с унаследованным кодом
При работе с существующей кодовой базой следует определиться с форматом работы: «поддержка» или «развитие». В первом случае не предполагается появление новой функциональности и доработка системы. Максимум — добавить несколько новых отчетов, пару форм тут и там. Во втором — есть необходимость значительной переработки предметной модели и / или архитектуры в целом. Если проект необходимо именно «поддерживать», а не «развивать», лучше следовать существующим правилам, независимо от того на сколько они удачные. Если перед вами откровенный говнокод, от предложения посупортить его лучше отказаться.
Развитие проекта — задача более сложная. Тема рефакторинга выходит за рамки данной статьи. Отмечу лишь два самых полезных паттерна: «антикоррупционный слой» и «душитель». Они очень похожи. Основная идея — выстроить «фасад» между старой и новой кодовыми базами и постепенно есть слона переписывать всю систему по кусочкам. Фасад берет на себя роль барьера, не позволяющего проблемам старой кодовой базы просочиться в новую и обеспечивающего отображение старой бизнес-логики в новую. Будьте готовы, что фасад будет состоять из сплошь из хаков, уловок и костылей и рано или поздно канет в лету вместе со всей старой кодовой базой.
DDD также обеспечивает основу для стратегического и тактического моделирования. Стратегическое проектирование позволяет точно определить наиболее важные области разработки на основе бизнес-ценностей. Тактическое моделирование нужно для построения работающей Доменной Модели с использование проверенных в бою строительных блоков и шаблонов.
Три столпа DDD
Domain-Driven Design это подход к разработке программного обеспечения, который сфокусирован на трёх основных принципах:
Единый язык (Ubiquitous Language)
Итак, как найти, изучить и запечатлеть этот особый язык, следующие подсказки помогут вам в этом:
Определение DDD
DDD это не серебряная пуля; как и все в программном обеспечении, всё зависит от контекста. Старайтесь использовать этот подход чтобы упростить ваш Домен (Domain), а не добавляйте сложности. Если разрабатываемое вами приложение ориентировано на работу с данными и ваши сценарии в основном подразумевают CRUD операции (создание, чтение, обновление, удаление), то вам не нужен DDD. Вам всего лишь нужен интерфейс манипуляцией данными в вашей хранилище.
Если в вашем приложении реализует менее 30 сценариев использования (Use Cases), может вам проще использовать фреймворки Symfony или Laravel, для управления всей бизнес логикой.
Однако, если ваше приложение имеет более 30 сценариев использования, ваша система подвержена движению в сторону Большого Комка Грязи (Big Ball of Mud). Если вы точно знаете что ваша система будет достаточно сложной, то вам следует рассмотреть возможность использования DDD для борьбы с этой сложностью.
Если вы знаете, что ваше приложение будет расти и, вероятно, часто изменяться, то DDD определенно поможет вам в контроле сложности и реализации рефакторинга вашей модели с течением времени.
Если вам не понятен домен (Domain), над которым вы работаете, потому что он новый и никто ранее не вкладывал средства в это решение, это может означать, что он достаточно сложен для того чтобы с ходу начать применять DDD. В этом случае вам стоит в первую очередь начать тесное взаимодействии с экспертами предметной области (Domain Experts) для построения правильной модели.
Некоторые нюансы
Применять DDD не просто. Необходимо время и усилия, чтобы построить бизнес-домен, создать терминологию, провести исследования и организовать сотрудничество с экспертами предметной области используя единый язык, без профессиональных терминов программистов.
Вам потребуется участие экспертов предметной области. Это в свою очередь потребует открытого, здорового и непрерывного диалога, чтобы успешно перенести их терминологию в модель программного обеспечения.
Вдобавок ко всему, вам придется приложить усилия, чтобы избежать технических моментов реализации на начальном этапе, а сосредоточиться в первую очередь о поведении объектов и создании единого языка (Ubiquitous Language).
Стратегический обзор
Подтверждено, что распределенные архитектуры увеличивают общую производительность компании, поскольку они определяют границы вашего продукта, которые будут разрабатывать целевые команды разработчиков.
Если вы хотите узнать больше о стратегической части DDD, вам следует взглянуть на первые три главы книги Вона Вернона «Реализация методов предметно-ориентированного проектирования» или книгу Эрика Эвинса «Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем»
Выводы
Реализация DDD требует усилий. Если бы это было легко, все писали бы качественный код. Будьте готовы, потому что вы скоро узнаете, как писать код, который при прочтении отлично описывает бизнес вашей компании.
Domain-Driven Design: стратегическое проектирование. Часть 1
Здравствуйте, хабрапользователи! В этой статье речь пойдет о предметно-ориентированном проектировании программного обеспечения с использованием, в первую очередь, стратегических шаблонов. Вторую часть – про тактическое проектирование – читайте здесь.
Данный подход использовал Вон Вернон в своей книге «Реализация методов предметно-ориентированного проектирования». Цель написания этой книги: дать возможность разработчикам совершить полет на самолете DDD (в детстве автор зачастую путешествовал со своей семьей на небольших самолетах). Вид с высоты дает более широкое представление о проблемах моделирования, не давая застрять в различных технических деталях. Наблюдая ландшафт DDD таким способом, можно осознать преимущества как стратегического, так и технического проектирования. Подробнее – под катом!
Основной целью применения DDD является получение высококачественной модели программного обеспечения, которая будет максимально точно отражать поставленные бизнес-цели. Для реализации этого требуется объединение усилий как разработчиков, так и экспертов в предметной области. Создание дружной и сплоченной команды позволяет получить большое количество преимуществ для бизнеса. Обмен знаниями между членами команды снижает шансы появления «тайного знания» о модели, достигается консенсус между экспертами предметной области в отношении различных понятий и терминологии, разрабатывается более точное определение и описание самого бизнеса.
Для того чтобы уравнять разработчиков и экспертов предметной области, чтобы было гораздо проще обмениваться полезными знаниями о предметной области, подход DDD предлагает применять общий набор терминов, понятий и фраз, который будет использоваться в общении между членами команды, и который позже отразится в исходном коде результирующей программы.
Единый язык
Наиболее важное для разработчика – это умение слушать экспертов, получать максимальное количество полезных знаний о предметной области. В то же время, эксперты также должны прислушиваться к разработчикам и их пожеланиям. Команда учится и растет вместе, если она действует сплоченно, получая более глубокое понимание бизнеса.
Приведу несколько примеров из книги:
В команде, обсуждая модель применения вакцины от гриппа в виде кода, произносят фразу наподобие: «Медсестры назначают вакцины от гриппа в стандартных дозах». Возможные варианты развития событий:
Очевидно, что первый вариант совсем не оптимален, второй – лучше, но не учитывает некоторые важные концепции. Окончательный вариант наиболее приемлем для поставленной задачи.
Очень важно понимать, что в рамках предметной области смысл определенного термина или фразы может сильно отличаться. Существует некая граница, в пределах которой понятия
единого языка имеют вполне конкретное контекстное значение.
Ограниченный контекст
Эта концептуальная граница называется ограниченный контекст (Bounded context). Это второе по значимости свойство DDD после единого языка. Оба эти понятия взаимосвязаны и не могут существовать друг без друга.
Итак, ограниченный контекст – это явная граница, внутри которой существует модель предметной области, которая отображает единый язык в модель программного обеспечения.
Контекст банковских услуг
Сводка поддерживает запись о дебиторских и кредиторских транзакциях, отображающих текущее финансовое состояние клиента с точки зрения банка → Сводка о текущих счетах или сводка о сберегательных счетах
Сводка – это совокупность литературных выражений об одном или нескольких событиях, произошедших за определенный период времени → На сайте amazon.com продается книга Into Thin Air: A Personal Account of the Mt. Everest Disaster
Предметная область, предметная подобласть, смысловое ядро
Это и есть основа для стратегического проектирования при подходе DDD.
Пространство задач и пространство решений
Предметные области состоят из пространства задач и пространства решений. Пространство задач позволяет думать о стратегической бизнес проблеме, которая должна быть решена, а пространство решений, сосредоточится на том, как реализуется программное обеспечение, чтобы решить бизнес проблему.
Итак, для оценки пространства задач в первую очередь необходимо:
Карта контекстов
Естественный ход событий – совпадение границ контекстов с организационным делением команды. Люди, работающие вместе, разделяют один общий контекст модели.
Существуют такие отношения между ограниченными контекстами и отдельными командами проекта:
Сначала рисуется простая карта контекстов с границами и связью между ограниченными контекстами :
Например, PayeeAccount – это тот же BankingAccount, но с другим поведением (нельзя получить баланс). Таким образом будет создан отдельный контекст учета расходов (expense tracking). Также отдельно, в приведенном примере, создается контекст онлайн сервисов банка (on-line banking services) (такие сервисы, например, как распечатка выписок банка).
Детализированная карта выглядит вот так:
Так как контекст профайлов веб-пользователей используется как готовый внешний модуль и он поставляется «как есть», здесь устанавливается отношение конформист (нижестоящий подчиняется вышестоящему).
Вывод
Спасибо за внимание!
Статью подготовили: greebn9k (Сергей Грибняк), wa1one (Владимир Ковальчук), silmarilion (Андрей Хахарев).