Перейти к основному содержимому

Пример


Рассмотрим простой пример - проект design развития электронной торговли. Он подготовлен для демонстрации основных возможностей протокода.

Ниже приведена интерактивная модель - наиболее очевидный и наглядный результат проектирования.

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

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

Интерактивная модель состоит из сотен связанных между собой диаграмм (UML, C4 и др.), которые генерируются автоматически (PlantUML) на основании единого лаконичного описания проекта на протокоде. Интерактивная модель позволяют увидеть проект в динамике и на всех уровнях.

Кроме диаграмм, модель включает в себя генерацию (SSG) документации (Docusaurus) с описаниями элементов модели (Markdown) и фрагментов на протокоде (snippet).

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

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


important

Предположим, что у нас есть исходное верхнеуровневое представление об ограниченных контекстах context и действующих лицах actor предметной области domain.

Ограниченные контексты

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

Это позволяет уменьшить сложность решения, избежать хаоса (dependency hell) и реализовать такие контексты как отдельные, слабосвязанные между собой (low coupling) системы, подсистемы или сервисы проектируемого решения. Таким образом, каждый контекст может иметь свой цикл разработки и план поставки, отдельные задачи, возможно, выделенную команду и владельца продукта, бюджет и план развития.

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

Пример ограниченных контекстов предметной области

  design Развитие электронной торговли {

  domain Электронная торговля {   context Управление идентификацией и доступом   context Управление витринами   context Управление заказами   ..   }

  }

important

Вместе с названием caption элемента модели можно указать его имя name и описание description.

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

important

Контексты хотя и отделены друг от друга, тем не менее, они связаны и могут использовать @ use друг друга, создавая зависимости.

Пример с именами и описаниями

  domain ECommerce | Электронная торговля: "дистанционная торговля в сети Интернет" {

  context IAM | Управление идентификацией и доступом: "единый вход и регистрация"

  context SM | Управление витринами: "предложение и выбор товаров" {   @ use Персонализация (IAM): "для персонализации витрин и стимулирования покупателей"   }

  context OM | Управление заказами: "оформление, отмена и возврат выбранных товаров" {   @ use Корзина (IAM): "для получения выбранных товаров"   @ use Профиль (IAM): "для получения данных покупателя"   }   ..   }

Действующие лица

important

В зависимости от решения, действующие лица actor (в дальнейшем пользователи решения) могут быть определены как внутри контекстов, так и на уровне предметной области.

Пример действующих лиц

  actor Visitor | Посетитель: "любой человек, использующий сайт или мобильное приложение" {   @ use Просматривает предложения (SM)   @ use Выбирает предложения (SM)

  - actor Guest | Гость: "незарегистрированный посетитель" {   @ use Регистрируется (IAM)   @ use Гостевой чекаут (OM)   }

  - actor User | Пользователь: "зарегистрированный посетитель" {   @ use Входит (IAM)   @ use Персональные предложения (SM)   @ use Чекаут с регистрацией (OM)   }

  }

Верхнеуровневый проект

important

Проектирование каждого ограниченного контекста на верхнем уровне состоит из формирования

  • статических структур structure на основе понятий предметной области
  • процессов поведения process контекста на основе сценариев взаимодействия с пользователями и другими контекстами

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

Пример верхнеуровневой схема

Верхнеуровневый проект является эффективным инструментом для предварительной оценки трудоемкости и сроков на этапе элаборации и анализа рисков.

Статические структуры

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

important

Такие активные структуры состоят из агрегатов aggregate (например, Заказ), которые обрабатываются в памяти, сохраняются и загружаются из хранилищ как единое целое.

Структуры понятий моделируют ограниченный контекст "изнутри", исходя из понятийного и терминологического анализа предметной области.

Пример статических структур

  domain Электронная торговля {

  context Управление заказами {

  structure Orders | Заказы: "омниканальные заказы клиентов" {

  aggregate Order | Заказ {

  string Номер   method Оформить

  - aggregate Заказ с доставкой {   string Адрес доставки   datetime Дата доставки   }   - aggregate Заказ с самовывозом {   string Место получения   }   }

  aggregate Заказчик {   string Телефон   string Почта   string ФИО   }

  aggregate Склад

  }   }   }

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

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

Процессы взаимодействия

important

Поведение контекста формируется из верхнеуровневых процессов прикладного взаимодействия process, которые включают конкретные сценарии использования useCase этого контекста различными действующими лицами actor и другими контекстами.

Пример процессов взаимодействия

  domain Электронная торговля {

  context IAM | Управление идентификацией и доступом {

  process Идентификация {   useCase Вход   useCase Регистрация   useCase Получение профиля   }

  }

  context SM | Управление витринами {

  process Шопинг {   useCase Просмотр карточки товара   useCase Просмотр листинга товаров   useCase Выбор товара   useCase Получение корзины   useCase Получение избранного   }

  process Стимулирование {   useCase Использовать бонусы   }

  }

  context OM | Управление заказами {

  process Оформление заказа {   useCase Оплата заказа   useCase Выбор доставки   useCase Размещение заказа   useCase Чекаут {   - useCase Гостевой чекаут   - useCase Чекаут с регистрацией   }   }

  }

  actor Visitor | Посетитель {   link Просматривает предложения (Просмотр листинга товаров, SM)   link Выбирает предложения (Выбор товара, SM)   link Просматривает (Получение избранного, SM)

  - actor Guest | Гость {   link Регистрируется (Регистрация, IAM)   link Гостевой чекаут (OM)   }

  - actor User | Пользователь {   link Входит (Вход, IAM)   link Персональные предложения (Использовать бонусы, SM)   link Чекаут с регистрацией (Гостевой чекаут, OM)   }

  }

  }

примечание

Связь между действующим лицом и сценарием является ассоциацией и указывается как link (см. ниже Отношения\Ассоциации)

Отношения

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

Пример детализации отношений

Внешние и внутренние зависимости

important

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

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

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

  • @ call Корзина(Получение корзины,SM).
important

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

Например, процесс идентификации IAM, при регистрации, создает учетную запись и профиль в структуре данных пользователя:

  • @ create Заполняет(Профиль)
  • @ create Создает(Учетная запись)
important

Внутри процессов одного контекста, сценарии так же зависят друг от друга, позволяя включать @ include, расширять @ extend и категоризировать различные варианты использования.

Например, зависимости между сценариями чекаутов при оформлении заказа:

  process Оформление заказа {

  useCase Checkout | Чекаут {   @ create Создает (Заказчик)   @ include (Выбор доставки)   @ include (Оплата заказа)   @ include (Размещение заказа)

  - useCase Guest Checkout | Гостевой чекаут {   @ include (Ввод данных о заказчике)   }   - useCase Чекаут c регистрацией: Чекаут {   @ call API (Получение профиля, IAM)   }

  }

  useCase Ввод данных о заказчике

  useCase Выбор доставки

  useCase Оплата заказа

  useCase Размещение заказа {   @ call API (Получение корзины, SM)   @ create Создает (Заказ)   }

 }

Ассоциации

important

Классы объектов (агрегаты, сущности, объекты-значения) а так же их содержимое объединяются друг с другом отношениями ассоциации (association). Это означает, что сами объекты этих классов связаны link друг с другом. Ассоциация определяется:

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

Например, Заказу принадлежат такие ассоциации:

  • оформил< link(Заказчик)
  • отгружен[1..*]> link(Склад)

В этом примере название ассоциации (оформил,отгружен) предполагает указание определенного порядка связанных элементов (например, Заказ отгружен> со Склада, или Заказ оформил< Заказчик) При этом, целевые классы (Заказчик,Склад) не принадлежат исходному классу (Заказ) - это отдельные, самостоятельные агрегаты, и каждый конкретный заказ имеет связь link c одним конкретным заказчиком и одним и более складом(ами).

Другим примером ассоциации у Заказа может быть Товарная позиция:

  • содержит[1..*]\ object Товарная позиция

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

important

Агрегаты имеют очень сильные ассоциации (composition, ownership - полностью владеют, содержат) с составляющими их частями. Так же агрегаты имеют объединяющие ассоциации (shared aggregation) с другими агрегатами в одном контексте.

  aggregate Order | Заказ {   string Номер   number Сумма   оформил< link (Заказчик)   отгружен> link (Склад)   содержит[1..*]\ object Товарная позиция {   string Артикул   string Наименование   string Количество   number Цена   number Сумма   }   method Оформить: "рассчитывает сумму заказа"   ..  }

примечание

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

Например, Гость и Гостевой чекаут связаны 1:1. Одновременно, в одном интерфейсе, нельзя оформлять несколько заказов. Так же несколько посетителей не могут сообща оформлять один и тот же заказ.

Внешние ссылки

important

Несмотря на то, что в контекстах обычно не используются прямые связи с внешними агрегатами (чтобы не создавать сильные зависимости между контекстами), тем не менее, агрегаты могут хранить ссылки reference на другие внешние агрегаты (для последующего выполнения объединяющих запросов (CQRS) и др.).

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

Например, товарная позиция в заказе может содержать идентификатор предложения из контекста управления витринами:

  • на основании> reference(Предложение по варианту, SM)

Объем работ


Проблема актуального проекта

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

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

Управление изменениями

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

important

Все изменения в объеме работ моделируются с помощью структурированного списка задач - backlog, который состоит из раделов epic, содержащих запросы на изменение changeRequest.

К запросам на изменение обычно относятся различные доработки, изменения конфигурации и т.п.:

  • новые функции feature
  • исправления дефектов bug
  • переключение toggle - включение или выключение определенной функциональности
  • заглушка stub - временная и примитивная реализация функции

Изменения в бэклоге могут начинаться с какого-то определенного состояния, описанного в модели domain, а могут - с чистого листа. В последнем случае, изначально, в модели domain будет пусто.

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

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

примечание

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

important

Внутри запросов на изменение находятся описания изменений @ change, которые необходимо внести в существующий проект.

  epic Shopping | Шопинг: "посетитель просматриват и выбирает товарные предложения" {

  feature Добавление товаров в карточке {

  @ change ($domain.SM) {

  Шопинг {   @ use Выбирает (Выбранные товары)   useCase Выбор товара {   @ extend Дополняет (Просмотр карточки товара)   @ use Изменяет (Список товаров): "добавление\удаление товара в корзину\избранное"   }

  }

  structure Selections | Выбранные товары: "корзина и списки избранного" {

  aggregate Список товаров {   содержит[*]\ link Товар (Предложение по варианту)

  - aggregate Корзина   }

  }   }

  @ change ($domain) {

  Посетитель {   @ use Выбирает предложения (SM)   }   }

  }

  feature Добавление товаров в листингах {   @ change ($domain.SM) {   process Шопинг {

  useCase Выбор товара {   @ extend Расширяет (Просмотр листинга товаров)   }

  }   }   }

 }

Поставка продуктов


important

Для моделирования поставки delivery решения необходимо определить состав продуктов product и их выпусков release. Продукты поставляют @ deliver эпики из бэклога, а выпуски применяют @ apply конкретные изменения.

Рассмотрим два подхода к определению состава продуктов:

Отдельные продукты

Комплексный продукт

Каждый контекст - это отдельный продукт со своим циклом поставки

Контексты объединяются в комплексный продукт с общим циклом поставки

Пример нескольких продуктов

  delivery Продукты {

  product Ваш каталог   product Ваш ИД   product Ваши витрины   product Ваши заказы

  product Ваша точка продаж   product Ваш КЦ

  product Ваше приложение   product Ваш сайт   }

Пример комплексного продукта

  delivery Продукты {   product Ваша Платформа   product Ваше приложение   product Ваш сайт   }

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

Чтобы не запутаться, избежать тупиков с выпусками (dependency hell) и ломающих изменений (breaking changes), необходимо для всех продуктов следовать общим правилам версионирования (например, semver) и обратной совместимости (backward compatibility).

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

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

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

Подходит для работы одной команды с небольшими контекстами и единым центром ответственности и управления

В основном, непрерывная интеграция и, возможно, развертывание (CI\CD) с тотальной обратной совместимостью. Эпизодически, при значительных изменениях, происходят управляемые релизы.

Эффективна комбинация управляемых релизов для мажорных версий и непрерывная интеграция\поставка - для минорных

примечание

В любом случае, поставляемое решение может компоноваться и как микросервисы и как монолит. Декомпозиция на продукты может не совпадать с разделением платформы на сервисы.

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

Как видно на диаграмме, между продуктами в итоге существует множество связей, которые, на первый взгляд, выглядят подозрительно (напоминая big ball of mud и dependency hell вместе). Это и в действительности может возникнуть, если поставлять все возможности во всех продуктах одновременно. Однако, наш проект предполагает эволюцию выпусков и, если разложить эту схему по релизам, она хоть и будет выглядеть сложно, но мы увидим четкий порядок и разделение выпусков.

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

Непрерывная поставка

important

Непрерывные интеграции и, возможно, непрерывные развертывания (CI/CD) нескольких продуктов происходят независимо друг друга. Хотя все продукты связаны между собой, в этом случае они всегда обратно совместимы compatible (backward compatible) и в номере версии обычно меняются только младшие цифры.

Это достигается за счет:

  • продуманного расширения существующих API продукта, либо их версионирования. При этом, другие продукты как бы "не замечают" изменений.
  • в случае связанных между собой изменений в разных продуктах, новые функции снабжаются переключателями (flag) и доставляются в выключенным состоянии. Когда все продукты будут обновлены, переключатели будут включены и произойдет переход к новой функциональности (cutover).

Для примера рассмотрим серию обратно совместимых релизов продукта Ваши витрины.

  • Storefront 0.0 . Изначально, продукт Ваши витрины является простым онлайн-каталогом без возможности выбора товаров. Он был уже разработан и поставлен до начала проекта.
Пример непрерывной поставки Storefront 0.0

  release Storefront 0.0 {   @ use Товары (PC 1.0)

  @ apply (Просмотр карточки товара, Scope)   @ apply (Просмотр листинга товаров, Scope)   }

  • Storefront 1.0 . Первый выпуск (фактически, прототип) продукта Ваши Витрины будет включать только самые важные изменения по выбору товаров в карточке и предоставлению корзины для оформления заказа. В данном случае, Ваш сайт должен использовать переключатели на эти возможности, чтобы не быть заблокированным и продолжать свои циклы CI\CD.
Пример непрерывной поставки Storefront 1.0

  release Storefront 1.0: "только важные фичи по выбору товаров и предоставлению корзины" {   @ compatible (Storefront 0.0)

  @ apply (Добавление товаров в карточке, Scope)   @ apply (Предоставление корзины, Scope)   }

  • Storefront 1.1 . Выпуск косметических, обратносовместимых улучшений API продукта Ваши витрины для реализации на сайте выбора товаров в листингах.
Пример непрерывной поставки Storefront 1.1

  release Storefront 1.1 (Доработки прототипа 1): "добавление товаров в листингах" {   @ compatible (Storefront 1.0)

  @ apply (Добавление товаров в листингах, Scope)   }

  • Storefront 1.2 . Следующий релиз Витрин будет включать работу с Избранным. Предположим для выбора товара как в корзину, так и в избранное используется одно API. В этом случае, если в первом релизе сделать заглушку (stub), Ваш сайт сможет включить, при необходимости, эту возможность еще раньше (сразу после предыдущего релиза).
Пример непрерывной поставки Storefront 1.2

  release Storefront 1.2 (Доработки прототипа 2): "работа с Избранным" {   @ compatible (Storefront 1.1)

  @ apply (Добавление в избранное, Scope)   @ apply (Просмотр избранного, Scope)

  }

  • Storefront 1.3 . Для выпуска фичи использования бонусов в продукте Ваши Витрины необходимо сделать переключатель - релиз может выйти как до запуска продукта Ваш ИД, так и после. Кроме этого, необходимо расширить API Получения корзины таким образом, чтобы Ваши Заказы смогли использовать корзину с бонусами.
Пример непрерывной поставки Storefront 1.3

  release Storefront 1.3: "с персонализацией" {   @ compatible (Storefront 1.2)   @ use Персонализация (ID 1.0)

  @ apply (Использование бонусов, Scope)   }

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

Управляемые выпуски

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

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

important

Создание новой ветки branch продукта позволяет решить две задачи:

  • Прервать серию обратной совместимости и полностью переработать продукт или группу продуктов;
  • Одновременно развивать некоторое время обе версии продукта (например, для проверки продуктовых гипотез или плавного перехода).

Для координации больших управляемых выпусков и достижения конкретных вех (milestone) в развитии решения используется План развития и разбиение работ на этапы.

План развития


Проблема развития

В рассматриваемом примере хорошо видно, что контексты заказов(OM) и витрин(SM) получают Профиль клиента и зависят от контекста управления идентификацией и доступом (IAM). Несмотря на то, что это слабые связи (low coupling), тем не менее, получается так, что функции идентификации и доступа являются критически важными для запуска. Для минимизации рисков, придется реализовать и интегрировать IAM максимальным ресурсом и как можно быстрее, чтобы своевременно выявить интеграционные дефекты.

Так же видно, что для управления заказами (контекст OM), кроме Профиля клиента(IAM), необходима его Корзина(SM).

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

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

Этапы выпуска

Однако, не все функции (и зависимости между ними) могут быть равноценны. Например, следующие допущения могут иметь место:

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

Соответственно, вместо одновременной поставки и интеграции, можно действовать последовательными этапами milestone в рамках общего плана работ roadmap:

  1. Онлайн-каталог. Прежде всего выпустить сервис витрин с минимальными возможностями - онлайн-каталог товаров без возможности регистрации и оформления заказов. Это позволит сразу приступить к последовательной шлифовке механизмов представления товарных предложений;
  2. Прототип. После этого реализовать ключевые сценарии выбора предложений и получения корзины, выпустить прототип с гостевым чекаутом (без получения профиля). Это позволит быстрее приступить к оформлению заказов и продажам;
  3. Удобный выбор. По мере исправления дефектов с представлением предложений и улучшением выбора товаров, может быть выпущен минорный релиз сервиса витрин. Например, с основной фичей - добавлением товаров в корзину не только из карточки товара, но и из листингов. сле этого реализовать ключевые сценарии выбора предложений и получения корзины, выпустить прототип с гостевым чекаутом (без получения профиля).
  4. Избранное. Еще один минорный релиз сервиса витрин для работы с избранным.
  5. Запуск. Выпуски Онлайн-каталога и Прототипа дадут достаточно времени для реализации и интеграции функций регистрации\входа (контекст IAM). После этого становится возможен полноценный запуск, включающий весь запланированный функционал.

Распределение релизов по этапам

Каждый этап milestone является изменением change в поставке deliver решения - он содержит в себе выпуски продуктов release, которые появляются при выполнении этапа. Переключаясь между этапами, вы определяете какие выпуски присутствуют на данный момент времени в поставке.

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

Рассмотрим для примера этап выпуска Прототипа и связанные с ним изменения.

  milestone Prototype | Прототип: "первый прототип, самое необходимое для оформления заказов" {   @ use (Онлайн-каталог)

  @ change ($delivery) {

  Ваши витрины {

  release Storefront 1.0: "только важные фичи по выбору товаров и предоставлению корзины" {   @ use (Storefront 0.0)

  @ apply (Добавление товаров в карточке, Scope)   @ apply (Предоставление корзины, Scope)   }

  }

  product Ordering | Ваши Заказы: "оформление, оплата и контроль исполнения заказов" {   @ use Корзина (Ваши витрины)   @ deliver (Оформление заказа, Scope)

  release Ordering 1.0 (Первый прототип) {   @ use Корзина (Storefront 1.0): "требуется предоставление корзины"

  @ apply (Гостевой чекаут, Scope)   }

  }   }  }

Аспекты развития

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

Архитектура решения


important

Поставка delivery выпущенных release продуктов product завершается их размещением @ deploy в системном ландшафте landscape (окружении других, интегрированных систем) для эксплуатации.

Системный ландшафт разграничивается boundary на изолированные зоны с определнными системами system внутри и правилами взаимодействия снаружи, которые реализуются как шлюзы (API Gateway), прокси, адаптеры, медиаторы и др.

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

Их можно условно разделить на несколько уровней (в соответствии с рекомендациями C4):

  1. Системный ландшафт - описывает взаимодействие между поставляемым решением и другими системами, определяет его место.
  2. Модули развертывания container (deployable unit) - все независимые, самостоятельные (в плане поставки) модули, необходимые для развертывания продуктов (приложения, сервисы, базы данных и др.), которые формируются на этапе разработки в соответствии с общими архитектурными принципами
  3. Компоненты - несамостоятельные части модулей развертывания.

Системный ландшафт

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

Пример системного ландшафта

  landscape Architecture | Системы: "взаимодействие систем, детализация до уровня контейнеров" {   @ use C4 Clean Architecture Микросервисы (Методология)   @ use Выражает контексты (Электронная торговля)   @ use Размещает продукты (Продукты)

  boundary Platform | Платформа электронной торговли {

  }

  boundary Enterprise Systems | Корпоративные системы {

  system PLM | Производство: "ввод и вывод продукции из производства"

  system ERP | Учетная система

  system CRM | Клиенты и лояльность

  system SCM | Логистика

  }

  boundary Domain Services | Прикладные сервисы {

  system Comm Service | Сервисы Коммуникаций

  system Geo Service | Сервис геолокации

  system Address Service | Сервис адресов

  system Payment Service | Сервис оплаты

  system Delivery Service | Сервис доставки

  }

  boundary Marketplaces | Маркетплейсы {

  system Ozon | Озон

  system Yandex | Яндекс Маркет

  system Sber | Сбермегамаркет

  }

  }

Модули развертывания

important

Размещение (развертывание) deploy продуктов в системном ландщафте осуществляется в модулях развертывания (deployable unit), так же иногда называемых контейнерами container (в определении С4, не docker).

  Платформа электронной торговли {

  boundary Ecommerce Services | Сервисы электронной торговли {

  container SSO | Сервис единого входа {   ..   @ deploy (Ваш ИД, Solution)   }

  container Storefront Service | Сервис витрин {   ..   @ deploy (Ваши витрины, Solution)   }

  container OMS | Подсистема заказов {   ..   @ deploy (Ваши заказы, Solution)   }

  }

  boundary Customer Apps | Приложения для покупателей {

  container Web App | Веб-приложение {   ..   @ deploy (Ваш сайт, Solution)   }

  container Mobile App | Мобильное приложение {   ..   @ deploy (Ваше мобильное приложение, Solution)   }

  }

  boundary Backoffice | Приложения для сотрудников {

  container POS Panel | Панель продавца {   ..   @ deploy (Ваша точка продаж, Solution)   }

  container CC Panel | Панель поддержки {   ..   @ deploy (Ваш КЦ)   }

  ..   }  }

important

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

  boundary Ecommerce Services | Сервисы электронной торговли {

  container SSO | Сервис единого входа {   ..   @ manifest (Управление идентификацией и доступом)   }

  container Storefront Service | Сервис витрин {   ..   @ manifest (Управление витринами)   }

  container OMS | Подсистема заказов {   ..   @ manifest (Управление заказами)   }

 }

Существует несколько вариантов связи между контекстами и модулями развертывания:

  • Один модуль - один контекст;
  • Один контейнер - часть контекста;
  • Один контейнер - разные контексты (или части).
important

В микросервисной архитектуре микросервис обычно воплощает один контекст.

Например, весь контекст (включая UI) управления идентификацией и доступом (IAM) может поставляться в одном контейнере - подсистеме управления заказами.

important

В многоуровневой архитектуре (например, Clean Architecture) контекст может воплощать как все уровни, так и только некоторые из них.

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

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

Развитие ландшафта

important

Источником изменений change системного ландшафта landscape (появления и размещения новых контейнеров container) являются запросы на изменение, в частности, новые фичи feature. Они порождают новые верхнеуровневые компоненты (процессы process и структуры structure), которые складываются в контейнеры для размещения deploy.

Например, новая возможность Единый вход и регистрация изменяет системный ландшафт, добавляя новый контейнер - Сервис единого входа (SSO).

  feature Единый вход и регистрация {   @ change ($domain) {

  context IAM | Управление идентификацией и доступом {

  process Identification | Идентификация: "вход, регистрация и предоставление пользовательских данных" {   ..   }

  structure Identities | Пользователи: "данные о пользователях электронной торговли" {   ..   }

  }

  }

  @ change ($landscape.platform.Сервисы электронной торговли) {

  container SSO | Сервис единого входа {   @ use Клиенты (CRM)

  @ manifest (Управление идентификацией и доступом)   @ deploy (Ваш ИД, Solution)   }

  }

 }

Компоненты

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

Компоненты для элаборации

В процессе элаборации и верхнеуровневого проектирования этого примера мы разделили компоненты на два верхнеуровневых слоя:

  1. Слой процессов - прикладной, динамический. Содержит процессы взаимодействия process и сценарии usecase. Cоответствует Application layer DDD;
  2. Слой структур - предметный, статический. Содержит агрегаты aggregate и другие объекты. Cоответствует Domain layer DDD.

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

Компоненты для разработки

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

  1. Слой процессов (Application layer DDD)
    • основывается на конкретной инфраструктуре для взаимодействия (например Kafka, RabbitMQ, ESB) и представления (например React,Angular, Web, Mobile). Соответствует Infrastructure layer DDD и внешней оболочке Clean Architecture;
    • содержит представления для взаимодействия с пользователями. Соответствует Application layer DDD и внешней оболочке UI Clean Architecture;
    • содержит адапетры для взаимодействия с другими системами и окружающим миром (контроллеры, презентеры, медиаторы и другие адаптеры), реализующие бизнес-логику приложений и сервисов, используя простые классы и репозитории из слоя структур. Соответствует Application layer DDD и слою Interface Adapters Clean Architecture;
    • содержит интеракторы, реализующие бизнес-логику приложений и сервисов, используя простые классы и репозитории из слоя структур. Соответствует Application layer DDD и слою UseCases Clean Architecture;
  2. Слой структур (Domain layer DDD)
    • cодержит простые классы (old plain class), реализующие основную бизнес-логику для объектов предметной области (агрегатов, определенных на верхнем уровне). Соответствует ядру Entities Clean Architecture;
    • содержит репозитории, интерфейсы для хранения объектов предметной области. Соответствует ядру Entities Clean Architecture;
    • основывается на конкретной инфраструктуре хранения (СУБД, облачные сервисы, файлы) и реализует репозитории для хранения агрегатов. Cоответствует Infrastructure layer DDD

Например, при таком подходе, шаблон проектирования агрегат aggregate будет выглядеть следующим образом: