Open-source Java-библиотека для пометки и автоматической проверки границ гексагональной архитектуры.
→ github.com/remodov/hexagonal-architecture
Теория и обоснование, зачем нужны порты и адаптеры — в отдельной статье Гексагональная архитектура. Эта страница — про библиотеку: что она даёт, как подключить, как настроить проверку.
Что внутри
Два модуля:
hexagonal-architecture-annotations— аннотации@Port,@Adapter,@UseCase,@DomainModelдля разметки классов и пакетов. Без рантайма, без зависимостей кроме чистой Java.hexagonal-architecture-archunit— ArchUnit-правила, которые на каждом запуске тестов проверяют, что:- доменный слой не зависит от Spring, JPA, Kafka и других инфраструктурных библиотек;
- адаптер реализует ровно один порт;
- use case вызывается только из адаптеров входа, не из доменных классов;
- аннотация
@DomainModelстоит на классах в пакетеdomain.*, и наоборот — все классы вdomain.*должны быть помечены.
Подключение
// build.gradle.kts
dependencies {
implementation("ru.vikulinva:hexagonal-architecture-annotations:1.0.0")
testImplementation("ru.vikulinva:hexagonal-architecture-archunit:1.0.0")
testImplementation("com.tngtech.archunit:archunit-junit5:1.3.0")
}
Разметка модулей
В типовом Gradle-multi-module-проекте Order Service структура такая:
order-service/
domain/ — @DomainModel: чистая Java, Entity, VO, агрегат
application/ — @UseCase: оркестрация, транзакции, доменные события
adapter-rest/ — @Adapter: REST-контроллеры (адаптер входа)
adapter-postgres/ — @Adapter: jOOQ-репозитории (адаптер выхода)
adapter-kafka/ — @Adapter: KafkaListener + KafkaProducer
port-orders/ — @Port: контракты, реализуемые adapter-*
На уровне пакетов:
@DomainModel
package com.example.order.domain;
import ru.vikulinva.hexagonal.DomainModel;
@UseCase("CreateOrder")
public class CreateOrderUseCase {
private final OrderRepository orders; // порт
private final OrderEventPublisher events; // порт
// ...
}
@Port
public interface OrderRepository {
Order findById(OrderId id);
void save(Order order);
}
@Adapter
public class JooqOrderRepository implements OrderRepository {
// jOOQ-имплементация — здесь живёт всё, что знает про PostgreSQL
}
Проверка на CI
В src/test/java/.../ArchitectureRulesTest.java:
@AnalyzeClasses(packages = "com.example.order")
class ArchitectureRulesTest {
@ArchTest
static final ArchRule domain_isolated =
HexagonalRules.domainHasNoInfrastructureDependencies();
@ArchTest
static final ArchRule adapters_implement_ports =
HexagonalRules.adaptersImplementSinglePort();
@ArchTest
static final ArchRule use_cases_called_from_inbound_adapters =
HexagonalRules.useCasesCalledFromInboundAdaptersOnly();
@ArchTest
static final ArchRule domain_classes_annotated =
HexagonalRules.allDomainPackagesAnnotated();
}
Прогон на каждом PR — четыре правила, нарушение каждого ловится с конкретным указанием класса.
Когда применять, когда нет
Библиотека соответствует 4-му уровню зрелости Use Case Pattern. Это сильный инструмент, но не бесплатный — требует разделения на Gradle-модули, дисциплины с импортами, дополнительных типов в коде. Брать стоит:
- на сервисах с большой долей бизнес-логики (Order, Payment, Reservation) — там, где есть инварианты домена и большая стоимость их нарушения;
- когда команда хочет проверять архитектурные правила на CI, а не в код-ревью;
- когда планируется замена инфраструктуры (например, jOOQ → Spring Data, REST → gRPC) без переписывания доменного слоя.
Не брать на:
- CRUD-сервисах и справочниках — там оверхед не окупается;
- сервисах из 2–3 use case'ов — однострочный сервис без порта читается легче, чем тот же код с тремя аннотациями.
Подробное объяснение, какие архитектурные проблемы решает Ports & Adapters и как сравнивать с классической слоёной архитектурой — в статье Гексагональная архитектура.
Лицензия
Apache 2.0 — можно использовать в коммерческих проектах без ограничений.