Domain Driven Design: давайте не будем усложнять
Рассказываем, что такое Domain-Driven Design — предметно-ориентированное проектирование. Рассматриваем плюсы и минусы, нюансы и инструменты.
41К открытий60К показов
Представьте, что к вам обращается клиент, который хочет создать веб-сервис для промышленного предприятия. Ему нужны модули для контроля производства, состояния склада, форма для приема заказов и оплаты, построение маршрутов и отслеживание машин, доставляющих продукцию. Если попытаться уместить все эти функции в одном приложении, неизбежно возникнет путаница: появится много лишнего кода, который будет мешать программе работать. В такой ситуации вспоминается «бритва Оккама»: не плоди лишних сущностей без надобности.
И у программистов есть инструменты, которые работают по этому принципу. Один из них — подход Domain-Driven Design, предметно-ориентированное проектирование. Мы в KozhinDev применяем DDD на практике и расскажем о его преимуществах в статье.
Что такое Domain-Driven Design
Domain-Driven Design — подход к проектированию ПО, в основе которого положено тесное сотрудничество клиента и разработчиков. Заказчик посвящает команду в бизнес-логику своей компании, объясняет, как устроена ее работа, участвует в проектировании сервиса. Основной принцип DDD — разделение приложения на домены.
Домен — предметная область, которая описывает совокупность проблем и целей бизнеса. Можно сказать, что это — отрасль деятельности компании заказчика, которую необходимо автоматизировать с помощью IT-решения. Например, если компания занимается грузовыми перевозками, в домен будут входить все связанные с этим проблемы: выбрать машину, построить маршрут и другие.
В проектируемом веб-сервисе выделяют также основной домен — core domain, ядро продукта, его основную функцию. Так, если продолжить пример с перевозкой грузов: основная задача приложения — обеспечить доставку посылки из пункта А в пункт Б.
Домены в свою очередь делятся на субдомены — подобласти, которые отвечают за отдельные проблемы. Например, для сервиса грузоперевозок в качестве субдомена оформление заказа и выбор оптимального маршрута.
Для решения проблемы могут использоваться модели (model), которые описывают отдельные аспекты предметной области. Пример — сценарии пользовательского поведения на сайте. Человек открывает главную страницу, ему нужно заказать перевозку — он нажимает на кнопку «отправить груз».
Впервые подход Domain-Driven Design описал Эрик Эванс еще в 2004 году в книге
«Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем». Более краткое изложение принципов Domain-Driven Design можно найти у Вона Вернона в издании «Предметно-ориентированное проектирование. Самое основное», которое вышло в 2016 году.
Зачем нужен Domain-Driven Design
Мы использовали подход Domain-Driven Design для создания информационной системы «Абитуриент», которая автоматизировала работу приемной комиссии Сибирского федерального университета. Этот сервис включает в себя личный кабинет оператора, личный кабинет абитуриента, возможность подачи документов онлайн, приема документов онлайн и офлайн, двустороннюю интеграцию с Порталом Госуслуг и другие возможности. В процессе проектирования возникали все новые и новые потребности, архитектура сервиса разрасталась.
Такое разрастание функционала грозит образованием «больших комков грязи» — big balls of mud. Это крупные массивы запутанного, неряшливого кода, которые снижают производительность сервиса и осложняют его поддержку в будущем.
Объясняем на котиках: если у длинношерстного кота образуется колтун, то ему будет некомфортно. Комок шерсти будет мешать ему в повседневной жизни, мешать. Можно попытаться распутать колтун, но обычно это не так уж легко и доставляет животному еще больше дискомфорта. Чаще всего спутанную шерсть приходится просто срезать и следить, чтобы она не путалась впредь.
Также и с крупными веб-сервисами: «комок» некачественного кода мешает ему нормально работать, даже разработчики не всегда могут разобраться в его назначении, но взять и «вырезать» его — сложная задача. Поэтому лучше предотвратить его появление на этапе проектирования — например, использовать DDD.
Основные инструменты Domain-Driven Design
Для упрощения работы со сложными системами в DDD используются:
- Ubiquitous Language — язык описания или универсальный язык. Он нужен, чтобы описание домена и моделей было однозначным, не возникало противоречий.
- Bounded Context — ограниченный контекст. Ограниченная предметная область, которая отвечает за строго определенный функционал, со своим языком описания.
Основной принцип работы по DDD — разделение предметной области на ограниченные контексты со своими языками описания. Так, без DDD модель «пользователь» описывает все роли, и поэтому очень разрастается. Она описывает и посетителей сайта (имя пользователя, адрес), и данные авторизации (когда пользователь зашел в систему), и разграничения прав доступа для модераторов. В DDD такая модель разделяется на отдельные модели для каждого ограниченного контекста, чтобы не возникало путаницы. Посетитель, модератор, администратор — это разные типы пользователей, каждый из которых относится к своей области.
Другой пример ограниченного контекста — отправка уведомлений через почту или смс. Это замкнутая область, которая пересекается с бизнес-моделью в четко определенных местах вызова функций отправки, и не использует модели из других областей.
С помощью Domain-Driven Design мы структурировали сервис для СФУ. Выделили главный домен — прием документов от абитуриентов из разных городов. Обособили личный кабинет абитуриента, личный кабинет оператора приемной комиссии, контентную часть — полезную информацию, которую добавляют модераторы, а также заверение документов простой электронной подписью без визита в вуз, систему документооборота, формирование списков и другие модули.
В итоге архитектура проекта получилась четкой и понятной:
- небольшие и понятные контексты;
- каждый контекст отвечает за свой свою подобласть;
- ясные и лаконичные названия сущностей.
Связи в Domain-Driven Design
Чтобы сервис корректно работал и выполнял все свои функции, между модулями системы нужно настроить связи. В DDD-подходе это называется context mapping.
Нужно помнить, что:
- у разных связей есть свои плюсы и минусы, но нет плохих или хороших связей в общем смысле.
- создание правильных связей между контекстами и обновление их в зависимости от ситуации — важный элемент DDD.
Типы связей в Domain-Driven Design:
- Партнерство (Partnership), общее ядро (Shared Kernel): используются для тесно связанных между собой контекстах. Обычно самые быстрые в реализации, но несут в себе опасность связанности кода. Механизм партнерства похож на слаженную работу команд для разработки фронтенда и бэкенда: они обмениваются информацией, подстраиваются под изменения друг друга. Общее ядро подходит для ситуаций, когда нужно использовать в разных контекстах одну сущность, например, пользователя.
- Потребитель-поставщик (Customer-Supplier), конформист (Conformist): нужны, когда для разных контекстов приходится использовать один универсальный язык. Потребитель-поставщик похож на партнерство, но выделяется главная команда — поставщик, которая не всегда отзывчива к изменениям другой команды — потребителя. Конформист — когда потребитель вообще никак не может влиять на то, что отдает ему поставщик.
- Антикоррупционный слой (Anticorruption Layer): применяется, когда нужна прослойка между универсальным языком источника данных. Более безопасный вид связи между поставщиком, у которого меняется API, и потребителем. Все данные от поставщика проходят через прослойку, меняется только она, а структура потребителя остается неизменной.
- Open Host Service, опубликованный язык (Published Language): используются на стороне источника данных для стандартизации протокола обмена и приема. Некоторые авторы разделяют их, но мы рассматриваем, как один вид. Эта связь обратна антикоррупционному слою: поставщик поддерживает постоянный API, чтобы потребителю не приходилось постоянно обновлять свои данные.
Кроме прямого вызова методов объектов, связи в DDD можно реализовать также с помощью механизма сообщений Messaging, событий — Domain Events, Event Sourcing. Доступно также создание сценариев через Event Storming. Домены реагируют на событие по-своему: например, если событие «пользователь создан», то модуль биллинга может создать ему аккаунт для оплаты, модуль отправки сообщений — прислать приветственное письмо.
Недостатки Domain-Driven Design
Главная сложность подхода DDD — необходимость работать в тесной связке с клиентом. Не все заказчики готовы выделить людей в своем штате, которые будут вводить разработчиков в курс дела, оставаться на связи, участвовать в проектировании. Если клиенту нужен сложный, многофункциональный продукт, то придется объяснять ему важность участия.
Также, если команда ранее не работала по Domain-Driven Design, то программистам придется изучать новые для себя инструменты, адаптироваться к более плотному сотрудничеству с клиентом. Например, разработчикам при использовании этого подхода нужно внимательнее подходить к созданию и изменению сущностей и связей, к переименованию.
Подведем итог: что дает клиенту и разработчику DDD
Сложное практически всегда состоит из простых частей, соединенных простыми связями. Благодаря применению Domain-Driven Design код веб-сервиса или мобильного приложения получается несложным и понятным. В итоге упрощается его чтение, а значит — поддержка и развитие сервиса в будущем.
Основные инструменты DDD — универсальный язык и ограниченный контекст. Но не обязательно использовать все инструменты, можно ограничиться основными и добавлять новые по мере необходимости. Даже простого разделения предметных областей, продумывание их перед разработкой поможет сделать код приложения более качественным. При развитии продукта важно продолжать придерживаться принципов DDD.
В растущих системах стоимость поддержки плохого кода вырастает в геометрической прогрессии, тогда как сложность планирования на старте разработки почти линейная при линейной сложности предметной области. Применение DDD делает поддержку сервиса не только проще для разработчика, но и дешевле для заказчика.
41К открытий60К показов