В интернете много статей про гексагональную архитектуру. Почти все они написаны по одному шаблону: сначала объясняется, что это магия, потом показывается проект с нуля на пустом месте, в конце — вывод «используйте это всегда». Я хочу показать другое. Я видел как команды тратили недели на переход со слоёной архитектуры на гексагональную — и в итоге получали тот же код, переложенный по другим папкам. Без новых тестов. Без новой изоляции. Только с новой терминологией на ревью. Начнём со слоёной Вот стандартная трёхслойная архитектура. Presentation → Application → Infrastructure. Хорошая слоёная архитектура — это уже почти гексагональная. Я докажу это кодом. - 1 Несколько важных деталей: • Сервисы 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), ни в репозиториях. Каждый класс делает одно — читает или пишет. Это упрощает навигацию по коду, облегчает ревью и естественным образом готовит архитектуру к масштабированию — если в будущем понадобятся отдельные модели чтения и записи, структура уже к этому готова. Теперь «рефакторинг» в гексагональную Вот что я сделал: Хорошая слоёная архитектура — это уже почти гексагональная. Я докажу это кодом. - 2 Изменений в логике — ноль. Ни одна строчка внутри сервисов не тронута. Ни одна строчка в репозиториях. Только пакеты переехали и получили новые имена: 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/