Обзор книги “What Is Domain-Driven Design?”

DDD или Domain Driven Design — это концепция введенная Эриком Эвансом в одноименной книги в 2003 года, а значит ей скоро исполнится 20 лет. Казалось бы такого срока достаточно для повсеместного принятия этой концепции, ведь она действительно стоящая, но нет. И мне кажется, что проблема в том, что достаточно сложно понять саму концепцию, а дальше начать ее применять. С первым пунктом нам поможет книга “What Is Domain-Driven Design?” за авторством Vladik Khononov, которую мы разберем здесь, а со вторым пунктом Event Storming, который мы подробно разберем в следующий раз, а в этот раз ограничимся кратким описанием.

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

Рис.1 “Содержание книги”

Strategic Design

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

Analyzing Business Domains

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

Рис.2 “Типы поддоменов”

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

Discovering Domain Knowledge

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

The ubiquitous language is a model of the business domain

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

Managing Complexity with Bounded Contexts

Иногда для одного и того же слова в ubiquitous language мы должны использовать уточнения, например, для пользователя заказ еды — это одно, для ресторана, куда пришел этот заказ — это второе, а для курьера, что доставляет еду — это что-то третье. В итоге, мы не можем использовать общий ubiquitous language для всей организации. Выходом становится разделение ubiquitous language на множество маленьких языков с прикреплением их к явным контекстам, которые мы будем называть bounded context. Интересно, что bounded context определяет границы ubiquitous language, в рамках которых термины являются консистентными. Размер этих контекстов не является решающим фактором при нарезке — главное, чтобы наша модель оставалась консистентной. Интересно отметить различие между bounded contexts и subdomains — поддомены являются частью бизнеса и поэтому они есть и их надо просто обнаружить, а контексты проектируются. Bounded Context определяет физические границы проектируемых систем, а также может служить границей владения и ответственности за систему (ownership boundary).

Context Mapping

Точки соприкосновения между bounded contexts называются контрактами. В этих местах команды, отвечающие за разные контексты, взаимодействуют друг с другом. Существуют следующие паттерны взаимодействия: cooperation, customer-supplier, separate ways.

В режиме cooperation у нас есть 2 опции:

  • partner, когда две команды владеют разными контекстами и могут просто договориться об изменениях в контракте. Причем договороспособность тут двухсторонняя и ни у кого нет переговорной силы форсировать свое решение
  • или shared kernel patterns, когда общие части разных доменов выделяют в отдельную часть и дальше ей владеют несколько команд. В общем и целом, это противоречит подходу с владением одним bounded context одной конкретной командой, поэтому возможны проблемы. Чтобы их не случилось ниже приведена рекомендация по имплементации этого паттерна

The key to implementing the shared kernel pattern is to keep the scope of the shared kernel small, and limited to the integration contract only

В режиме customer-supplier поставщик сервиса (service provider) называется upstream и потребитель сервиса downstream. В этой конфигурации возможны разные распределения влияния между поставщиком и потребителем и для каждого варианта есть свой паттерн.

Рис. 3 “Схемы взаимодействия в режиме customer-supplier”

Separate ways — это способ коммуникации, который состоит в ее отсутствии, так как команды идут своим путем. Причинами для этого могут быть:

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

Отдельно стоит сказать, что подход separate ways не стоит использовать для проблем из core subdomains.

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

  • High-level design — обзор компонентов системы и моделей, который они реализуют
  • Communication patterns — можно будет увидеть паттерны коммуникации разных команд и предпочтительные паттерны интеграции из перечисленных выше
  • Organizational issues —можно будет увидеть организационные моменты во взаимодействии между consumers и producers

Дальше мы переходим к

Tactical Design

И начнем с главы про

Business Logic Implementation Patterns

Автор выделяет 4 паттерна: transactional script, active record, domain model и event-sourced domain model. Подробнее про них ниже

Рис. 4 “Паттерны имплементации бизнес-логики”

Architectural Patterns

Автор выделяет 3 вида архитектур. На самом деле вариантов больше и про них можно прочесть в книге “Fundamentals of Software Architecture” от Neal Ford, Mark Richards (по ссылке мой обзор этой книги). Если возвращаться к видению автора, то вот 3 его архитектуры:

  • слоеная архитектура (layered architecture)
  • порты и адаптеры (ports & adapters)
  • command & query responsibility segregation (CQRS)
Рис.5 “Архитектурные паттерны”

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

Рис.6 “Подробнее про CQRS”

Integration of Bounded Contexts

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

Рис.7 “Паттерны интеграции”

DDD in Practice

Дальше мы переходим к тому, как начать применять DDD на практике.

Event Storming

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

Рис.8 “Шаги техники event strorming”

Пройдемся по этим шагам:

  • unstructured exploration — на этом шаге в режиме брейншторма все участники группы самостоятельно накидывают на доску domain events
  • timelines — сгенерированные на предыдущем шаге domain events выстраиваются в хронологическом порядке, начиная с happy path
  • commands — на этом шаге добавляются commands, которые описывают что именно триггерит событие или поток событий. У части команд есть actor, который и запускает выполнение команды
  • policies — на этом шаге идет разбор команд, которые не имеют actor. У таких команд есть policy, когда запускается такая команда, обычно она завязана на наступление какого-то другого domain event
  • external systems — на этом шаге модель расширяется внешними системами, которые не являются частью домена, что разбирается, но которые участвуют в процессе, например, исполняют command или получают нотификации о domain events
  • aggregates — когда все команды и события на месте, участники могут начать задумываться об оптимизации и выделении aggregates, которые получают команды и генерируют события
  • bounded contexts — на последнем шаге время посмотреть на всю картину. Группы тесно связанных aggregates являются естественными кандидатами на определение границ для bounded contexts

В общем, Event Storming является очень хорошим инструментом для совместного моделирования сложной предметной области .

Evolutionary Design

Бизнес компании меняется, поэтому поддомены внутри организации могут менять свои типы, которых всего три: core, generic, supporting. Например, могут происходить переходы вида

  • coregeneric, например, когда другая компания выходит на рынок с коробочным решением или SaaS сервисом, который повторяет core логику компании
  • generic core, например, когда стандартная активность внутри компании реализуется так хорошо, что ее можно начать продавать наружу как сервис
  • supporting core

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

  • transaction script active record
  • active recorddomain model
  • domain modelevent-sourced domain model

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

  • shared kernelpartnership, так как bounded contexts, принадлежащие раньше одной команде разъезжаются по разным, то имеет смысл переходить к партнерству
  • partnership customer-supplier — по мере роста организации коммуникации между командами слабеют и все сложнее поддерживать партнерские отношения и они переходят к отношениям поставщик-потребитель
  • customer-supplierseparate ways — по мере нарастания коммуникационных проблем схема поставщик-потребитель становится все менее привлекательной и команды переходят к самостоятельному развитию
  • separate wayspartnership or customer-supplier — когда generic или supporting subdomain становится core, появляется потребность унифицировать работу над ним и уйти от его параллельного развития разными командами

Интересно, что качество знаний о доменной логики напрямую влияет на размер bounded contexts

  • если domain logic неясна, то проектируй bounded contexts с широкими границами
  • если domain logic стабильна, то проектируй bounded contexts с более узкими границами, которые могут быть реализованы посредством микросервисов

Getting Started

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

  • Ubiquitous Language
  • Business Domain
  • Context Map
  • Bounded Contexts
  • Event Storming
  • Tactical Patterns: Value Object, Transaction Boundaries, Event-Sourced Domain Model

Итого

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

Источники

  1. What Is Domain-Driven Design? — book by Vladik Khononov
  2. Introducing EventStorming — book by Alberto Brandolini
  3. Fundamentals of Software Architecture — book by Neal Ford, Mark Richards, ссылка на мой обзор
  4. Domain-Driven Design: Tackling Complexity in the Heart of Software — book by Eric Evans
  5. Domain-Driven Design Distilled — book by Vaughn Vernon

Director of digital ecosystem development department at Tinkoff. Bachelor at applied math, Master at system analysis, Postgraduate studies at economics.

Director of digital ecosystem development department at Tinkoff. Bachelor at applied math, Master at system analysis, Postgraduate studies at economics.