Гексагональная архитектура и Domain Driven Design

Небольшое введение

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

Когда мы соотносим DDD и архитектуру, многие из нас думают о микросервисах. Но это не единственная реализация концепции DDD.

Гексагональная архитектура также является хорошим кандидатом для правильной реализации DDD, и это очень хорошая архитектура в целом, описанная Робертом К. Мартином в его книге: Чистая архитектура

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

Необходимые условия

  • JDK 11 или более ранняя версия
  • Maven 3+
  • Любая IDE или текстовый редактор

Что такое DDD?

Как уже объяснялось выше, технология Domain Drive Design была представлена Эриком Эвансом в 2003 году. Это подход к разработке программного обеспечения, при котором домен находится в центре нашего основного набора программного обеспечения. Все решение должно вращаться вокруг бизнес-правил и их терминологии.

Нам необходимо создать Вездесущий Язык между всеми акционерами (команда Домена, технические команды). Каждая терминология в этом языке должна встречаться в приложении. Например, если мы создаем приложение для отслеживания расписания самолетов, мы пытаемся найти в приложении сущности типа «Airplane» или «Travel» и объекты значений типа «Destination» или «Arrival».

Для получения дополнительной информации о DDD я советую вам прочитать эту бесплатную электронную книгу https://www.infoq.com/minibooks/domain-driven-design-quickly.

Дайте мне шестиугольник

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

Но это не единственное. Как говорит Марк Ричардс об архитектурных решениях в своей книге «Основы архитектуры программного обеспечения»: «Это всегда компромисс». Действительно, микросервис — не единственная возможная реализация. Для этого типа архитектуры необходимо иметь экосистему для управления (например, инструменты управления контейнерами, такие как Kubernetes или OpenShift) и потребность в большой масштабируемости. Каждое архитектурное решение имеет свои минусы. Для микросервисов надежность и производительность могут стать проблемой.

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

Итак, что же такое шестиугольная архитектура?

Это архитектура программного обеспечения, представленная Алистером Кокберном в 2005 году. Ее также называют «архитектурой портов и адаптеров». Цель состоит в том, чтобы иметь четкое разделение между вашим кодом домена (в отдельном проекте или модуле) и вашим вспомогательным кодом (например, доступ к базе данных, вызов API или Framework, как Spring в Java).

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

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

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

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

Реализация

Мы создадим новое приложение Spring boot с очень простым примером, чтобы проиллюстрировать эту архитектуру.

Вы найдете исходный текст в репозитории Github.

Это приложение, получающее информацию о фильмах из внешнего публичного API.

Мы создадим проект Maven с movies в качестве корня и 3 подмодулями, как показано ниже.

movies
├───movies-app
├───movies-domain
└───movies-infra-api

Как вы видите, у нас есть один основной модуль movies-app, один модуль Domain movies-domain и один модуль infra movies-infra-api. Каждый infra представляет детали инфраструктуры, такие как доступ к базе данных и вызов внешнего API.

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

Первое правило гексагональной архитектуры очевидно:

  • Домен ни от чего не зависит. Все зависит от него.

Второе — :

  • Вы не используете инфрамодуль напрямую, вы должны пройти только через модуль Domain.

Таким образом, между двумя инфрамодулями нет прямой зависимости. Ни один из них не знает о существовании другого.

Следовательно, как Домен сможет использовать infra-api, если не будет зависеть от них?

Правила зависимостей

Конфигурация pom.xml предотвратит случайное соединение модулей.

Для соблюдения правил зависимостей для Hexagonal Architecture необходимо, чтобы часть dependencies из pom.xml movies-domain выглядела следующим образом:

...
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.8.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.8.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>
...

Как видите, ничего о Spring, о персистентности или внешнем API. Это неизменное правило, Домен зависит от
ничего. Только то, что нужно для компиляции (здесь, например, Lombok) или запуска тестов.

Как мы можем использовать другие модули?

Домен будет раскрывать интерфейсы, а каждый инфрамодуль будет их реализовывать. Как таковой, Домен просто заботится об использовании своих
интерфейсов и не заботится о том, кто и как их будет реализовывать.

Для зависимости всех инфра, мы должны импортировать модуль Domain с областью provided, как показано ниже:

...
<dependencies>
    ...
    <dependency>
        <groupId>com.architecture.hexagonal.example</groupId>
        <artifactId>movies-domain</artifactId>
        <scope>provided</scope>
    </dependency>
    ...
</dependencies>
...

А для модуля приложения movies-app, нам нужно будет импортировать Domain и все остальные модули, как показано ниже:

...
<dependencies>
    <dependency>
        <groupId>com.architecture.hexagonal.example</groupId>
        <artifactId>movies-domain</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>com.architecture.hexagonal.example</groupId>
        <artifactId>movies-infra-api</artifactId>
        <scope>runtime</scope>
    </dependency>
    ...
</dependencies>
...

Для всех инфрамодулей мы должны использовать область видимости runtime. Это предотвращает случайное использование модулей непосредственно в других инфрамодулях.

Иногда инфрамодуль нуждается в другом модуле. Для этого он должен пройти через Domain вместо того, чтобы использовать его напрямую. Вы не должны находить infra-зависимости в других infra-модулях. Именно «приложение» организует все infra-зависимости для создания связи.

Домен

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

Что должно делать ваше приложение? Зачем его создавать?

Ответ: : Фильмы

Мы хотим получать информацию о фильмах, такую как название, описание, рейтинг, режиссер, актеры…

Итак, давайте начнем с создания нашего Ubiquitous Language, который мы должны найти в нашем приложении впоследствии.

Повсеместный язык

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

В нашем случае это будет что-то вроде этого:

  • Фильмы : Представляет собой список фильмов
  • Название : Название фильма
  • Synopsys: объяснение фильма с небольшим количеством синопсисов.
  • Ставка : Скорость фильма
  • Режиссер : Человек, который создал фильм
  • Актеры Лица, играющие в фильме
  • Дата выхода : Дата выхода фильма

Коэффициент должен быть от 0 до 5

Api Фильмы

Для этой цели мы будем использовать открытый API TheMovieDB, с библиотекой, предоставленной Хольгером Брандлом в этом репозитории.

Я уже создал поддельный аккаунт для этой статьи. Токен помещается в application-local.yml проекта. Если токен не работает, вы можете создать учетную запись и получить новый токен: https://www.themoviedb.org/signup.

Как только все будет хорошо для этой подписки на API, вы можете напрямую запустить приложение Spring Boot и протестировать его.

Выставление наших собственных API

Наш API предоставляет 3 API, которые сопоставлены с API Movie DB. Мы сопоставляем данные с нашей собственной моделью данных.

  • ПОЛУЧИТЬ http://localhost:8080/api/v1/movies/populars
  • GET http://localhost:8080/api/v1/movies/upcoming
  • GET http://localhost:8080/api/v1/movies/[id]

Первый дает вам список фильмов, отсортированных по популярности, второй — список фильмов, которые только предстоят, и последний — подробную информацию о фильме с id, переданным в параметре path.

Архитектура

Для архитектуры мы, очевидно, используем архитектуру Hexagonal Architecture с Apache Maven для создания модуля и сборки нашего приложения.

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

В модуле movie-app мы имеем главный класс для запуска приложения Spring Boot и управления нашим небольшим API.

Схема представляет собой архитектуру для лучшей читаемости структуры проекта.

Ниже мы объясним каждый модуль более подробно.

Модуль домена

В модуле Domain мы создаем пакет movies, который позволяет нам создавать различные Domain или subdomain части в одном и том же ограниченном контексте. Будьте внимательны к этому контексту, вы должны уважать его, чтобы не смешивать различные домены, которые не позволяют развиваться вместе.

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

В этом случае необходимо создать два разных доменных артефакта (.jar для Java).

В этом модуле домена мы найдем подпакеты для управления модулем, например, пакет error для обработки исключений, пакет model для представления бизнес-модели. В этом пакете шаблонов вы должны найти вездесущий язык.

У вас также есть пакет service, который представляет всю бизнес-логику. Все, что ваше приложение должно представлять в виде функциональных возможностей, должно находиться в этом пакете.
И для завершения, у вас есть outbound paquage. Этот пакет содержит все интерфейсы, используемые пакетом service. Они представляют все функциональные возможности, не охваченные доменом, такие как доступ к базе данных, вызов api… Именно эти интерфейсы будут реализованы внешним инфра-модулем.

Модуль приложений

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

Как и модуль Domain, мы нашли пакет для управления ошибками (объявление и обработка ошибок).

Пакет конфигурации собирает все конфигурации Spring Boot, в нашем случае у нас есть только Bean Configuration для объявления службы модуля Domain (помните, Domain ничего не знает о Spring context).

Мы также нашли пакет для контроллера для наших открытых API.

И, очевидно, у нас есть главный класс DemoApplication для запуска нашего приложения Spring Boot.

Модуль Infra

Наконец, у нас есть модуль infra, который собирает все конфигурации и сервисы для вызова внешних API.

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

Обычный пакет управления ошибками и пакет Service, которые представляют функции, необходимые Домену для правильной работы.

Класс TheMovieDBService реализует интерфейс домена MoviesProvider, найденный в пакете outbound Domain.

Заключение

Я надеюсь, что эта статья поможет читателям понять основы чистой архитектуры с реализацией DDD : Hexagonal Architecture. Как уже объяснялось в этой статье, выбор архитектуры — это всегда компромисс, и разработчикам или архитекторам не обязательно постоянно следовать этой реализации.

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

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

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

Когда команда разработчиков программного обеспечения понимает это, она может противостоять любым вызовам, связанным с бизнес-правилами.

Репозиторий Github: https://github.com/KevinDupeyrat/Hexagonal_Architecture_Article/tree/master/movies

Источник: https://link.medium.com/guu8azI4pxb

Оцените статью
Procodings.ru
Добавить комментарий