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 реализации для всех исходящих взаимодействий. Это снижает хрупкость тестов, повышает покрытие реального поведения системы и в конечном итоге ускоряет разработку, давая команде уверенность при рефакторинге сложной бизнес-логики. Архитектура перестаёт быть просто диаграммой и становится работающим инструментом для обеспечения качества на каждом этапе.
Эволюция Hexagonal Architecture: современные паттерны для безупречного тестирования от экспертов
Анализ современных подходов к тестированию в рамках Hexagonal Architecture на основе опыта экспертов. Рассмотрены эволюция паттерна, смещение акцента на интеграционное тестирование срезов, работа с in-memory адаптерами, обработка событий и правильная организация тестовой сборки приложения для достижения скорости и надёжности.
8
1
Комментарии (12)