Эволюция Hexagonal Architecture: современные паттерны для безупречного тестирования от экспертов

Анализ современных подходов к тестированию в рамках Hexagonal Architecture на основе опыта экспертов. Рассмотрены эволюция паттерна, смещение акцента на интеграционное тестирование срезов, работа с in-memory адаптерами, обработка событий и правильная организация тестовой сборки приложения для достижения скорости и надёжности.
Hexagonal Architecture (Портсы и Адаптеры), предложенная Алистером Кокберном, была создана для изоляции бизнес-логики от внешних зависимостей. Её первоначальная цель — облегчение тестирования — сегодня актуальна как никогда. Однако за годы практики эксперты столкнулись с новыми вызовами: микросервисы, event-driven дизайн, облачные сервисы. Это привело к естественной эволюции паттерна, появлению новых практик и уточнению старых, направленных на достижение истинной «безупречности» в тестировании.

Классическая гексагональная архитектура делит систему на три части: внутреннее ядро (домен), порты (интерфейсы) и адаптеры (реализации, соединяющие порты с внешним миром). Тестирование в такой парадигме фокусируется на ядре — его можно тестировать в полной изоляции, подменяя все адаптеры заглушками (stubs, mocks). Однако эксперты отмечают, что чрезмерное увлечение моками привело к новой проблеме: хрупким тестам, которые проверяют не поведение системы, а то, как были вызваны моки. Они не ловят регрессии в интеграции между ядром и реальными адаптерами.

Современный подход, который пропагандируют такие эксперты, как Матиас Нобак, — это смещение акцента с изолированного unit-тестирования ядра на интеграционное тестирование целых «срезов» или «сценариев использования» (Use Case). В этом случае тестируется не отдельный агрегат или сервис в домене, а целый порт с его первичным адаптером (например, HTTP-контроллер) и вторичными адаптерами, заменёнными на in-memory реализации. Создаётся тестовая сборка приложения, где репозиторий базы данных заменён на репозиторий в оперативной памяти (InMemoryRepository), а клиент внешнего API — на заглушку. Такой тест проверяет весь поток данных от входящего запроса до результата, обеспечивая высокую уверенность и при этом оставаясь быстрым, так как не требует поднятия реальной БД или сети.

Ещё одно ключевое обновление — явное разделение адаптеров на «входящие» (driving) и «исходящие» (driven). Для тестирования это имеет критическое значение. Эксперты рекомендуют создавать для каждого исходящего порта (например, интерфейса IOrderRepository) как минимум две реализации: одну «продакшен» (например, PostgreSQLRepository), другую «тестовую» (InMemoryOrderRepository). Тестовая сборка приложения внедряет in-memory адаптеры. Это позволяет проводить сквозное (end-to-end) тестирование бизнес-сценариев без инфраструктурных издержек. При этом сами адаптеры должны тестироваться отдельно, но это уже узкие интеграционные тесты, которых будет относительно мало.

С появлением событийно-ориентированной архитектуры в Hexagonal Architecture добавился новый тип портов и адаптеров — для событий. Эксперты предлагают рассматривать шину событий (Event Bus) как исходящий порт. В ядре определён интерфейс IEventPublisher. В продакшене его адаптером будет клиент Kafka или RabbitMQ. В тестовой сборке — простой in-memory event publisher, который просто сохраняет опубликованные события в список для последующей проверки в тестах. Это позволяет легко тестировать сложную событийную логику: убедиться, что после выполнения команды «Создать заказ» было опубликовано событие OrderCreated с правильными данными.

Важный урок от экспертов — отказ от тестирования через пользовательский интерфейс в рамках гексагональной архитектуры. UI считается всего лишь одним из многих возможных входящих адаптеров. Основное тестирование должно вестись на уровне портов. Например, если есть порт для создания пользователя, то пишутся тесты, которые вызывают этот порт напрямую (как если бы это сделал контроллер). Это даёт скорость и стабильность. UI-тесты (например, Selenium) выносятся в отдельный, высший уровень пирамиды тестирования и их количество минимизируется.

Практическим инструментом, воплощающим эти принципы, является использование современных фреймворков для внедрения зависимостей (DI), таких как Spring (для Java) или подобных в других языках. Контейнер DI настраивается дважды: один раз для продакшена (с реальными адаптерами) и один раз для тестов (с in-memory адаптерами). Это делает конфигурацию тестовой среды декларативной и простой.

Таким образом, эволюция Hexagonal Architecture для тестирования движется по пути большей практичности и целостности. Вместо догматичного следования «чистому» unit-тестированию с моками, эксперты предлагают строить быстрые, надёжные и максимально приближённые к реальности тестовые сценарии, используя in-memory реализации для всех исходящих взаимодействий. Это снижает хрупкость тестов, повышает покрытие реального поведения системы и в конечном итоге ускоряет разработку, давая команде уверенность при рефакторинге сложной бизнес-логики. Архитектура перестаёт быть просто диаграммой и становится работающим инструментом для обеспечения качества на каждом этапе.
8 1

Комментарии (12)

avatar
kzjoo5m9icme 27.03.2026
Отличный взгляд на эволюцию! Особенно про event-driven дизайн.
avatar
k6l98m88twma 28.03.2026
Интересно, как это сочетается с DDD и bounded context?
avatar
9zuyukg 28.03.2026
Не упомянули Clean Architecture. Это следующий шаг эволюции?
avatar
041zw7o9and5 28.03.2026
Актуально. В микросервисах эта архитектура спасает от хаоса.
avatar
cbw5z1cllx 29.03.2026
Слишком абстрактно. Хотелось бы больше примеров кода.
avatar
np85jg 30.03.2026
Эволюция есть, но базовый принцип изоляции ядра — неизменен и гениален.
avatar
d1g02xlqy 30.03.2026
Спасибо! Жду продолжения про CQRS и Event Sourcing в этом контексте.
avatar
dz6kg0m5aw47 30.03.2026
Ядро без зависимостей — это мечта любого тестировщика. Работает!
avatar
pk41uxe8 30.03.2026
Главное — не переусердствовать с слоями, иначе будет over-engineering.
avatar
1bcxw77z 30.03.2026
Статья для senior-разработчиков. Новичкам будет сложно.
Вы просмотрели все комментарии