АрхитектураАрхитектура ПОАрхитектор программного обеспечения

В слоистой архитектуре бизнес правила начинают зависеть от деталей внешних систем. Какой механизм гексагона...

В слоистой архитектуре бизнес-правила начинают зависеть от деталей внешних систем. Какой механизм гексагональной архитектуры меняет направление этой зависимости?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Гексагональная архитектура меняет направление зависимости через порты и адаптеры: бизнес-ядро определяет абстракции, необходимые ему для работы, а инфраструктурные компоненты реализуют их. Поэтому бизнес-правила зависят от стабильных портов, а не от баз данных, фреймворков или внешних API.

Исторический контекст

Традиционная слоистая архитектура часто строилась так, что верхние слои вызывали нижние: пользовательский интерфейс обращался к прикладному слою, тот — к инфраструктурному. При таком устройстве бизнес-логика постепенно начинала знать о формате хранения данных, сетевых клиентах и особенностях платформы.

Гексагональная архитектура, также известная как Ports and Adapters, возникла как способ сохранить бизнес-логику независимой от способов ввода, вывода и интеграции. Исходная проблема состояла не в самом наличии слоёв, а в том, что наиболее изменчивые технические детали становились источником направляющей зависимости для ядра системы.

Постановка проблемы

Если бизнес-код напрямую зависит от инфраструктуры, изменение хранилища, транспорта или внешнего поставщика может потребовать изменения доменных правил. Это повышает стоимость тестирования, усложняет замену технологий и создаёт риск проникновения технических ограничений в предметную модель.

Например, доменная операция может быть сформулирована через понятие сохранения агрегата, но вместо этого знать о конкретной таблице, ORM-модели или сетевом клиенте. Тогда граница модуля определяется техническим API, а не потребностями бизнеса.

Подробное решение

В центре располагается бизнес-ядро. Оно содержит доменные правила и формулирует порты — интерфейсы или абстракции, описывающие необходимые ему взаимодействия: загрузку сущности, сохранение результата, публикацию события или получение времени.

Входные порты представляют операции, которые система предоставляет внешнему миру. Их вызывают адаптеры пользовательского интерфейса, обработчики сообщений или контроллеры. Выходные порты описывают зависимости ядра от внешнего мира; их реализуют адаптеры базы данных, брокера сообщений, файловой системы или удалённого сервиса.

Ключевой механизм — инверсия направления зависимости. На уровне исходного кода реализация порта находится снаружи, но её контракт принадлежит ядру. Инфраструктура подстраивается под нужды бизнес-логики, а не наоборот.

Это не означает, что внешние системы перестают влиять на архитектуру. Их ограничения нужно преобразовывать в адаптерах: например, переводить внешний формат ошибки во внутренний результат или сопоставлять внешнюю модель с доменной. Иначе детали интеграции всё равно просочатся в ядро.

Преимущества подхода — более изолированное тестирование бизнес-правил, возможность заменять адаптеры и более явные границы. Компромисс состоит в появлении дополнительных абстракций и преобразований моделей. Для простой системы такая структура может быть избыточной, а плохо спроектированный порт способен лишь скрыть инфраструктурную связанность, не устранив её.

Ситуация из практики

В платёжной системе доменная операция подтверждения платежа напрямую использовала клиент конкретного платёжного провайдера. После смены провайдера пришлось менять бизнес-сценарий, обработку ошибок и тестовые стенды.

Рассматривались три варианта. Первый — оставить прямую зависимость и заменить клиент через условие; это дёшево краткосрочно, но увеличивает связанность. Второй — вынести весь платёжный код в отдельный сервис; это изолирует провайдера, но добавляет сетевую сложность и не всегда оправдано. Третий — ввести выходной порт платежного шлюза и адаптеры для каждого провайдера.

Выбрали третий вариант: доменное ядро стало работать с понятным ему контрактом, а преобразование запросов и ошибок выполнялось адаптерами. В результате смена провайдера ограничилась новым адаптером и настройкой маршрутизации; бизнес-сценарий и его тесты не потребовали изменений.

Что кандидаты часто упускают

  1. Меняет ли порт направление зависимости только потому, что он назван интерфейсом?

Нет. Важны владелец контракта и направление архитектурного знания. Если интерфейс объявлен инфраструктурным слоем и бизнес-код подстраивается под его технические типы, формальная абстракция не создаёт инверсию. Порт должен выражать потребность ядра и не зависеть от деталей конкретного адаптера.

  1. Нужно ли создавать отдельный порт для каждого внешнего вызова?

Нет. Порт должен соответствовать устойчивой потребности бизнес-сценария, а не механическому вызову конкретного API. Слишком мелкие порты увеличивают количество связей и усложняют сопровождение, а слишком общие превращаются в универсальный технический интерфейс, скрывающий детали вместо их изоляции.

  1. Изолирует ли гексагональная архитектура систему от всех изменений внешних требований?

Нет. Она изолирует прежде всего направление технических зависимостей и локализует преобразование внешних моделей. Если меняется само бизнес-правило или смысл операции, изменения должны попасть в ядро. Кроме того, неудачно выбранный порт может закрепить неправильную модель и сделать эволюцию сложнее, поэтому границы нужно пересматривать вместе с изменением доменных потребностей.