Шестиугольная архитектура с репозиторием

#hexagonal-architecture

#шестиугольная архитектура

Вопрос:

Я пытаюсь понять шестиугольную архитектуру на примере Repository . В этой настройке у меня есть следующие уровни: фреймворк (инфраструктура) -> приложение -> домен.

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

Это та часть, где мне становится сложно. Я хочу реализовать логику UserRepository , но для меня не имеет смысла реализовывать это на прикладном уровне, потому что контекст сохранения находится на уровне инфраструктуры (например JdbcUserRepository , или JpaUserRepository ). Но если я правильно понимаю шестиугольную структуру, я не могу реализовать UserRepository интерфейс непосредственно на своем инфраструктурном уровне, потому что инфраструктурный уровень не должен знать о доменном уровне.

Чего мне не хватает?

Ответ №1:

Я думаю, что путаница, с которой вы сталкиваетесь, связана с тем фактом, что вы пытаетесь подойти к уже существующему трехуровневому приложению с точки зрения шестиугольной архитектуры.
Давайте начнем с простого.
Давайте на мгновение забудем, что такое «Прикладной уровень».
У вас есть свой шестиугольник, который, если я правильно понимаю, содержит домен вашего приложения (пользовательский объект).
Правильно вы определяете порт внутри вашего шестиугольника, который позволяет вам извлекать пользователя из другого места. (Я говорю об UserRepository интерфейсе)
Все реализации вашего порта ( JdbcUserRepository или JpaUserRepository ) будут представлять адаптеры вашего порта и должны находиться вне вашего шестиугольника, чтобы не связывать низкоуровневые сведения об адаптерах с политиками более высокого уровня вашего шестиугольника.
Вот и все.
Вероятно, самое сложное — понять, что должно входить в ваш шестиугольник, а что нет, начиная с приложения с трехуровневой архитектурой (или какой-то …).
Храните внутри шестиугольника то, что полностью связано с вашим доменом и не связано с инфраструктурой.
Перемещение за пределы все связано с внешним миром, но не содержит никакой бизнес-логики.
Отделите и переместите его соответствующим образом, все, что имеет оба вышеуказанных контекста.

Комментарии:

1. Одна вещь, которую я не могу понять, — это какой смысл размещать реализацию, подобную JpaUserRepository на уровне инфраструктуры. Например, User findByName(String userName) метод будет реализован в основном как qry = em.createQuery("select u from User u where u.name = ?1"); qry.setParameter(1, userName); return qry.getSingleResult(); . Он использует только язык домена и абстрактный инфраструктурный API (JPA API и JP-QL DSL). В чем проблема с размещением его на уровне домена, что было бы в случае обычной многоуровневой архитектуры? Разве гексагональная архитектура не является излишеством?

2. Это становится еще сложнее понять, если учесть, что многие запросы реального мира неизбежно встраивают в них бизнес-логику. Пример: select e from SomeEntity e where e.status = :Requested and not exists (...) . Некоторые из этих запросов довольно длинные и содержат множество отдельных бизнес-правил. Писать их за пределами уровня домена не кажется правильным…

3. Я не думаю, что это перебор. Есть много причин, по которым лучше избегать размещения деталей инфраструктуры внутри основного домена. Основной из них: сцепление. Каковы преимущества размещения кода базы данных в собственном адаптере? Многие: — разделение обязанностей: если мне нужно изменить способ извлечения данных из моей базы данных, мне не нужно изменять класс / модуль относительно моей бизнес-логики; — индивидуальная «возможность разработки»: например, разработчик может работать над улучшением производительности запросов, а другой — над ошибкой в бизнес-логике, без больших проблем

4. — возможность индивидуального развертывания: вы можете доставлять отдельные модули без необходимости доставлять все приложение сразу

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

Ответ №2:

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

Вы разработали свой бизнес-уровень как шестиугольник. Что ж! Это означает, что ваша реализация зависит от контракта (= интерфейса API), который доступен извне. В той же идее ваша реализация использует другие интерфейсы для связи с внешним миром (= интерфейс SPI, который вы назвали UserRepository). Благодаря этим интерфейсам ваш шестиугольник изолирован от внешнего мира. Когда ваш шестиугольник будет создан, реальные реализации вашего SPI должны быть переданы в качестве параметров (инверсия управления).

Теперь, на вашем уровне инфраструктуры, вы будете реализовывать свою логику Jpa, реализуя адаптер (шаблон адаптера).

Ваш адаптер (который может быть, например, сервисом Spring) будет реализовывать ваш интерфейс UserRepository и обернет ваш JpaUserRepository. Затем каждый метод из вашего UserRepository будет перенаправлен на соответствующий метод вашего JpaUserRepository (с небольшой адаптацией или без нее).

введите описание изображения здесь