Создаём приложение с чистой архитектурой на Java 11

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

Архитектура программного обеспечения − важная тема программной инженерии последних лет. На практике реализация приложений с чистой архитектурой часто вызывает затруднения. Мы рассмотрим возникающие проблемы с точки зрения Java-разработчиков.

Перед погружением в код посмотрим на архитектуру:

Создаём приложение с чистой архитектурой на Java 11

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

Ключевые понятия:

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

Мы будем использовать Gradle и модули Java Jigsaw для обеспечения зависимости между различными слоями.

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

  • создание пользователя;
  • поиск пользователя;
  • обработка списка всех пользователей;
  • авторизация пользователя с паролем.

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

Так выглядит проект:

Создаём приложение с чистой архитектурой на Java 11

Давайте погрузимся в разработку.

Внутренние уровни

Наши объекты и сценарии разделены на два проекта: «domain» и «usecase».

Разрабатываем приложения с чистой архитектурой

Эти пакеты − сердце нашего приложения.

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

Пакет entity содержит все сущности. В нашем случае будет только один пользователь:

Модуль usecase содержит бизнес-логику. Начнем с простого сценария FindUser:

У нас есть две операции, которые извлекают пользователей из хранилища, что стандартно для сервис-ориентированной архитектуры.

UserRepository − это интерфейс, который не реализован в текущем проекте. Это деталь нашей архитектуры, а детали реализовываются на внешних уровнях. Мы реализуем UserRepository при создании сценария использования (например, с помощью внедрения зависимости). Это даёт нам следующие преимущества:

  • Бизнес-логика не изменяется в зависимости от реализации.
  • Любое изменение в реализации не затрагивает бизнес-логику.
  • Очень легко вносить серьезные изменения в реализацию.

Сделаем первую итерацию сценария CreateUser:

Как и в сценарии FindUser, нам нужен репозиторий, способ генерации идентификатора и способ кодирования пароля. Всё это − детали, которые будут реализованы позже на внешних уровнях.

Нам нужно проверить данные пользователя и убедиться в том, что он не был создан ранее. Пишем последнюю итерацию сценария:

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

Последний сценарий LoginUser довольно прост и доступен на GitHub.

Для обеспечения границ оба пакета используют модули Jigsaw. Они позволяют не раскрывать детали реализации. Например, нет причин раскрывать класс UserValidator:

Ещё раз о роли внутренних уровней:

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

Внешние уровни

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

Создаём приложение с чистой архитектурой на Java 11

Начнем с репозитория.

Репозиторий

Реализация UserRepository с простым HashMap:

Другие адаптеры

Адаптеры реализованы таким же способом, как интерфейс, объявленный в domain, и доступны на GitHub:

Теперь нужно собрать детали реализации вместе. Для этого создаём папку с конфигурацией приложения и папку с кодом для запуска приложения.

Так выглядит конфигурационный файл:

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

Ниже представлен класс запуска приложения:

Если потребуется использовать Spring Boot или Vert.x, нужно:

  • Создать новую конфигурацию веб-приложения.
  • Создать новый ApplicationRunner.
  • Добавить контроллеры в папку адаптера. Контроллер отвечает за связь с внутренними уровнями.

Вот как выглядит контроллер Spring:

Полный пример этого приложения для Spring Boot и Vert.x доступен на GitHub.

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

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

Плюсы

  • Мощность: ваша бизнес-логика защищена, и ничто извне не выведет её строя. Ваш код не зависит от внешнего фреймворка, контролируемого кем-то другим.
  • Гибкость: любой адаптер можно заменить в любое время любой другой реализацией на выбор. Переключение Spring boot/Vert.x происходит очень быстро.
  • Отложенные решения: можно построить бизнес-логику, не зная деталей о будущих БД и фреймворках.
  • Высокий уровень поддержки: легко определить компонент, вышедший из строя.
  • Быстрая реализация: архитектура позволяет сосредоточиться и уменьшить стоимость разработки.
  • Тесты: модульное тестирование проводится легче, так как зависимости четко определены.
  • Интеграционные тесты: можно реализовать любой внешний сервис для использования во время интеграционных тестов. Например, если не хочется платить за базы данных в облаке, можно использовать реализацию адаптера в памяти.

Минусы

  • Обучение: архитектура может стать непреодолимым препятствием для джуниоров.
  • Больше классов, больше пакетов, больше проектов, и с этим ничего не сделать. Изучайте другие языки, например, Kotlin. Он поможет уменьшить количество создаваемых файлов.
  • Высокая сложность проекта.
  • Не подходит маленьким проектам.

Понравилась статья о разработке приложений с чистой архитектурой? Другие статьи по теме:

Источник: Создание приложений с чистой архитектурой на языке программирования Java 11 на Medium

proglib.io

Добавить комментарий

Ваш e-mail не будет опубликован.

пятнадцать − 5 =