В интернете много статей про гексагональную архитектуру.
Почти все они написаны по одному шаблону: сначала объясняется, что это магия, потом показывается проект с нуля на пустом месте, в конце — вывод «используйте это всегда».
Я хочу показать другое.
Я видел как команды тратили недели на переход со слоёной архитектуры на гексагональную — и в итоге получали тот же код, переложенный по другим папкам.
Без новых тестов. Без новой изоляции. Только с новой терминологией на ревью.
Начнём со слоёной
Вот стандартная трёхслойная архитектура. Presentation → Application → Infrastructure.
Несколько важных деталей:
• Сервисы package-private — снаружи видны только интерфейсы CommandUseCase и QueryUseCase.
• Репозитории тоже package-private — слой приложения работает только с ReadRepository и WriteRepository.
• Профили Spring (jdbc, jooq) подключают нужную реализацию — слой приложения об этом не знает.
• Зависимости инвертированы. Бизнес-логика не знает ни про JDBC, ни про jOOQ.
Профили Spring (jdbc, jooq) — это не просто конфигурация.
Это и есть подмена адаптеров без изменения единой строки бизнес-логики.
Слой приложения работает с ReadRepository и WriteRepository — ему безразлично, что за ними стоит: JDBC, jOOQ или любая другая реализация.
Именно это гексагональная архитектура называет заменяемыми адаптерами.
В слоёной архитектуре с правильно расставленными интерфейсами — это уже работает.
Паттерн DAO здесь работает именно так: интерфейс репозитория — это и есть порт (пример на GitHub -
https://github.com/architectural-styles/pattern-dao-sample
).
В тестах его реализация подменяется на in-memory заглушку (фейковый репозиторий), и доменная логика тестируется без поднятия базы данных — ровно так же, как это декларирует гексагональная архитектура.
Такая структура обеспечивает полное тестовое покрытие на всех уровнях:
• Unit-тесты — сервисы тестируются без Spring и без БД, через FakeReadRepository и FakeWriteRepository.
• Slice-тесты (@WebMvcTest) — каждый контроллер тестируется изолированно, с замоканным use case.
• Интеграционные тесты (@SpringBootTest + MockMvc) — полный стек с реальной БД, без поднятия HTTP-сервера.
• E2E-тесты (@SpringBootTest + RestTestClient, RANDOM_PORT) — реальный HTTP от запроса до базы.
• Архитектурные тесты (ArchUnit) — границы слоёв проверяются автоматически: presentation не зависит от infrastructure, domain не зависит ни от чего.
Это не бонус. Это следствие правильно расставленных интерфейсов — тех самых, которые в гексагональной архитектуре называются портами.
Разделение на CommandUseCase / QueryUseCase и WriteRepository / ReadRepository — это лёгкий CQRS без отдельных баз данных и событий.
Он даёт практическую выгоду уже на уровне структуры: команды и запросы не перемешиваются ни в контроллерах (RestCommandController / RestQueryController), ни в репозиториях.
Каждый класс делает одно — читает или пишет.
Это упрощает навигацию по коду, облегчает ревью и естественным образом готовит архитектуру к масштабированию — если в будущем понадобятся отдельные модели чтения и записи, структура уже к этому готова.
Теперь «рефакторинг» в гексагональную
Вот что я сделал:
Изменений в логике — ноль.
Ни одна строчка внутри сервисов не тронута.
Ни одна строчка в репозиториях.
Только пакеты переехали и получили новые имена: presentation → adapters/in, infrastructure/api → ports/out.
В чём тогда разница?
Она есть, но она концептуальная, а не техническая.
Слоёная архитектура думает вертикально: запрос входит сверху и проходит вниз через слои. Разделение — по технической роли (представление, логика, данные).
Гексагональная думает от центра наружу: есть ядро с бизнес-логикой, и есть всё остальное, что к нему подключается.
Разделение — по направлению зависимостей (внутрь vs снаружи). HTTP, JDBC, jOOQ — это адаптеры. Они заменяемы. Ядро об этом не знает.
Разница становится ощутимой когда:
• У вас несколько входных адаптеров: REST API + gRPC + CLI + очередь сообщений.
• Вы хотите явно выразить в структуре пакетов архитектурное намерение: «это порт, это адаптер, это ядро».
• Команда большая и важно исключить случайные зависимости между слоями на уровне самой структуры пакетов, а не только через ArchUnit.
Для CRUD-сервиса с одним REST API — разница почти нулевая.
Да, в этой статье домен намеренно простой.
На сложном домене с богатой бизнес-логикой, агрегатами и доменными событиями гексагональная архитектура раскрывается полнее — ядро становится больше, и его изоляция от инфраструктуры приобретает больший вес.
Но цель здесь другая: показать, что правильно построенная слоёная архитектура уже содержит в себе все механизмы этой изоляции.
Усложнение домена не меняет этот вывод — оно лишь увеличивает ставки.
Вывод
Если вы строите слоёную архитектуру правильно — с интерфейсами на границах слоёв, с инверсией зависимостей, с package-private реализациями — вы уже получаете 90% преимуществ гексагональной.
Переход занимает час. Это переименование пакетов, а не переписывание логики.
Это значит одно из двух: либо ваша слоёная архитектура уже достаточно хороша, либо переход в гексагональную не стоит того страха, которым его окружают.
Выбирайте архитектуру под задачу, а не под моду.
Оба проекта — на GitHub. Посмотрите сами: структура пакетов разная, код одинаковый.
• https://github.com/architectural-styles/architecture-layered-sample
• https://github.com/architectural-styles/architecture-hexagonal-sample
Оригинал опубликован на LinkedIn —
https://www.linkedin.com/pulse/well-structured-layered-architecture-already-almost-hexagonal-russu-vy3wc/
Несколько важных деталей:
• Сервисы package-private — снаружи видны только интерфейсы CommandUseCase и QueryUseCase.
• Репозитории тоже package-private — слой приложения работает только с ReadRepository и WriteRepository.
• Профили Spring (jdbc, jooq) подключают нужную реализацию — слой приложения об этом не знает.
• Зависимости инвертированы. Бизнес-логика не знает ни про JDBC, ни про jOOQ.
Профили Spring (jdbc, jooq) — это не просто конфигурация.
Это и есть подмена адаптеров без изменения единой строки бизнес-логики.
Слой приложения работает с ReadRepository и WriteRepository — ему безразлично, что за ними стоит: JDBC, jOOQ или любая другая реализация.
Именно это гексагональная архитектура называет заменяемыми адаптерами.
В слоёной архитектуре с правильно расставленными интерфейсами — это уже работает.
Паттерн DAO здесь работает именно так: интерфейс репозитория — это и есть порт (пример на GitHub -
https://github.com/architectural-styles/pattern-dao-sample
).
В тестах его реализация подменяется на in-memory заглушку (фейковый репозиторий), и доменная логика тестируется без поднятия базы данных — ровно так же, как это декларирует гексагональная архитектура.
Такая структура обеспечивает полное тестовое покрытие на всех уровнях:
• Unit-тесты — сервисы тестируются без Spring и без БД, через FakeReadRepository и FakeWriteRepository.
• Slice-тесты (@WebMvcTest) — каждый контроллер тестируется изолированно, с замоканным use case.
• Интеграционные тесты (@SpringBootTest + MockMvc) — полный стек с реальной БД, без поднятия HTTP-сервера.
• E2E-тесты (@SpringBootTest + RestTestClient, RANDOM_PORT) — реальный HTTP от запроса до базы.
• Архитектурные тесты (ArchUnit) — границы слоёв проверяются автоматически: presentation не зависит от infrastructure, domain не зависит ни от чего.
Это не бонус. Это следствие правильно расставленных интерфейсов — тех самых, которые в гексагональной архитектуре называются портами.
Разделение на CommandUseCase / QueryUseCase и WriteRepository / ReadRepository — это лёгкий CQRS без отдельных баз данных и событий.
Он даёт практическую выгоду уже на уровне структуры: команды и запросы не перемешиваются ни в контроллерах (RestCommandController / RestQueryController), ни в репозиториях.
Каждый класс делает одно — читает или пишет.
Это упрощает навигацию по коду, облегчает ревью и естественным образом готовит архитектуру к масштабированию — если в будущем понадобятся отдельные модели чтения и записи, структура уже к этому готова.
Теперь «рефакторинг» в гексагональную
Вот что я сделал:
Изменений в логике — ноль.
Ни одна строчка внутри сервисов не тронута.
Ни одна строчка в репозиториях.
Только пакеты переехали и получили новые имена: presentation → adapters/in, infrastructure/api → ports/out.
В чём тогда разница?
Она есть, но она концептуальная, а не техническая.
Слоёная архитектура думает вертикально: запрос входит сверху и проходит вниз через слои. Разделение — по технической роли (представление, логика, данные).
Гексагональная думает от центра наружу: есть ядро с бизнес-логикой, и есть всё остальное, что к нему подключается.
Разделение — по направлению зависимостей (внутрь vs снаружи). HTTP, JDBC, jOOQ — это адаптеры. Они заменяемы. Ядро об этом не знает.
Разница становится ощутимой когда:
• У вас несколько входных адаптеров: REST API + gRPC + CLI + очередь сообщений.
• Вы хотите явно выразить в структуре пакетов архитектурное намерение: «это порт, это адаптер, это ядро».
• Команда большая и важно исключить случайные зависимости между слоями на уровне самой структуры пакетов, а не только через ArchUnit.
Для CRUD-сервиса с одним REST API — разница почти нулевая.
Да, в этой статье домен намеренно простой.
На сложном домене с богатой бизнес-логикой, агрегатами и доменными событиями гексагональная архитектура раскрывается полнее — ядро становится больше, и его изоляция от инфраструктуры приобретает больший вес.
Но цель здесь другая: показать, что правильно построенная слоёная архитектура уже содержит в себе все механизмы этой изоляции.
Усложнение домена не меняет этот вывод — оно лишь увеличивает ставки.
Вывод
Если вы строите слоёную архитектуру правильно — с интерфейсами на границах слоёв, с инверсией зависимостей, с package-private реализациями — вы уже получаете 90% преимуществ гексагональной.
Переход занимает час. Это переименование пакетов, а не переписывание логики.
Это значит одно из двух: либо ваша слоёная архитектура уже достаточно хороша, либо переход в гексагональную не стоит того страха, которым его окружают.
Выбирайте архитектуру под задачу, а не под моду.
Оба проекта — на GitHub. Посмотрите сами: структура пакетов разная, код одинаковый.
• https://github.com/architectural-styles/architecture-layered-sample
• https://github.com/architectural-styles/architecture-hexagonal-sample
Оригинал опубликован на LinkedIn —
https://www.linkedin.com/pulse/well-structured-layered-architecture-already-almost-hexagonal-russu-vy3wc/
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ