«Дизайнер знает, что он достиг совершенства не тогда, когда нечего добавить, а когда нечего убирать»

Антуан де Сент-Экзюпери

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

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

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

Цель этой статьи - популяризировать DDD и сосредоточить внимание на стратегических концепциях, которым часто уделяется меньше всего внимания при применении этого подхода.

Золотой молоток для золотых ногтей

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

  1. Скрипты транзакций
  2. Модели предметной области

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

У такого подхода есть свои преимущества: простота и скорость реализации. Недостатки могут появиться по мере роста системы. Признаки проблемы могут проявляться в следующем:

  • Сущности не контролируют правильность изменения своего состояния;
  • Увеличение побочных эффектов в бизнес-сценарии, которые не могут быть выполнены в разумные сроки в рамках одной транзакции;
  • Анемичные модели, когда большая часть логики разбросана по классам обслуживания, а классы моделей остаются без поведения, что приводит к тому, что разработчики теряют из виду общую картину и связь с предметной областью.

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

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

Если временной период предполагает активную разработку в течение трех или более лет, вам следует обратить внимание на другие требования, а также принять во внимание количество человеческих ресурсов, необходимых для завершения проекта. Наиболее подробная информация о сложности, присущей программным системам, описана в книге Грэди Буча [4] в главе «Сложность».

Практичный дизайн

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

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

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

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

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

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

Часть системы, которая инкапсулирует бизнес-процессы из одного или нескольких поддоменов, но не нарушает целостность универсального языка, называется ограниченным контекстом. При правильно разложенной системе нет необходимости реализовывать все части системы с использованием подхода DDD. Для отдельных частей можно использовать более простой вариант со скриптом транзакции. Еще одно преимущество такой декомпозиции - возможность использовать готовые решения для универсальных поддоменов.

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

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

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

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

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

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

После анализа предметной области были выделены следующие контексты:

  • адаптация
  • сотрудничество
  • управление развитием
  • уведомления
  • идентификация пользователя

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

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

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

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

На метр, можем ли мы его просто переместить?

Пизанская башня - красивая и знаковая достопримечательность, ежегодно привлекающая миллионы туристов. Но требующийся постоянный ремонт обходится правительству Италии очень дорого. Башня была спроектирована так, чтобы наклоняться, потому что, когда строительство началось в 1173 году, на месте не было достаточно людей, которые имели какой-либо архитектурный опыт или знания, поэтому они просто построили то, что хорошо выглядело в то время, не убедившись, что все будет работать с гравитацией. Прошло почти 100 лет, прежде чем кто-либо понял, что была совершена ошибка; но вместо того, чтобы исправить это сразу, мы позволили им продолжить строительство других зданий вокруг этого нового конструктивного недостатка, который со временем приводит к разного рода проблемам, включая трещины от землетрясения и опускание фундамента из-за использования более тяжелых строительных материалов, чем тогда. .

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

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

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

Заполнение

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

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

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

Майкл Лазко, старший серверный разработчик QuantumSoft

Глоссарий

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

Домен - это область знаний, влияния или деятельности.

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

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