← Назад к вопросам

Почему гексагональная архитектура называется архитектурой портов и адаптеров?

1.8 Middle🔥 202 комментариев
#JavaScript Core

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Гексагональная архитектура: почему «порты и адаптеры»?

Гексагональная архитектура (Hexagonal Architecture), предложенная Алистером Кокберном, действительно более точно описывается альтернативным названием — «Архитектура портов и адаптеров» (Ports and Adapters). Это название лучше отражает её суть и механику, в то время как «гексагональная» — это лишь метафора визуального представления. Давайте разберем, почему термины «порты» и «адаптеры» являются ключевыми.

Суть концепции: Изоляция ядра приложения

Основная идея архитектуры — изолировать ядро бизнес-логики (Domain или Application Core) от внешних деталей: пользовательских интерфейсов, баз данных, внешних API, сторонних сервисов и т.д. Это достигается за счет двух ключевых абстракций: портов (Ports) и адаптеров (Adapters).

Что такое порты (Ports)?

Порт — это интерфейс, который определяет контракт взаимодействия между ядром приложения и внешним миром. Это «дверь» или «точка входа/выхода» для ядра. Порты бывают двух типов:

  • Первичные (входящие) порты (Driving Ports): Определяют, как внешние акторы (пользователи, системы) могут взаимодействовать с ядром. Обычно это интерфейсы сервисов приложения (Use Cases). Например:
    // TypeScript: Первичный порт для работы с заказами
    interface OrderService {
        placeOrder(orderData: OrderRequest): Promise<OrderConfirmation>;
        cancelOrder(orderId: string): Promise<void>;
        getOrderStatus(orderId: string): Promise<OrderStatus>;
    }
    
    Ядро реализует этот интерфейс, содержа бизнес-логику.

  • Вторичные (исходящие) порты (Driven Ports): Определяют, какие операции ядру требуются от внешних систем (например, для сохранения данных или отправки уведомления). Это интерфейсы репозиториев, шлюзов, клиентов. Например:
    // TypeScript: Вторичный порт для хранения данных о заказах
    interface OrderRepository {
        save(order: Order): Promise<void>;
        findById(id: string): Promise<Order | null>;
        findByUserId(userId: string): Promise<Order[]>;
    }
    
    Ядро зависит от этого интерфейса, но не от его конкретной реализации.

Что такое адаптеры (Adapters)?

Адаптер — это конкретная реализация, которая «адаптирует» внешнее воздействие или технологию к ожиданиям порта. Адаптер подключается к порту, как штекер в розетку. Соответственно, адаптеры тоже бывают двух видов:

  • Первичные (входящие) адаптеры (Driving Adapters): Внешние акторы обращаются к ядру через них. Они преобразуют внешний запрос (HTTP, CLI-команду, сообщение из очереди) в вызов метода порта ядра.

    // TypeScript: HTTP-адаптер (контроллер) для первичного порта OrderService
    import { Request, Response } from 'express';
    
    class OrderController {
        constructor(private orderService: OrderService) {}
    
        async createOrder(req: Request, res: Response) {
            const orderData = req.body;
            // Адаптер преобразует HTTP-запрос в вызов порта ядра
            const confirmation = await this.orderService.placeOrder(orderData);
            res.status(201).json(confirmation);
        }
    }
    
  • Вторичные (исходящие) адаптеры (Driven Adapters): Реализуют интерфейсы вторичных портов, адаптируя вызовы ядра к конкретной технологии (БД, внешнему API, почтовому сервису).

    // TypeScript: Адаптер репозитория для MongoDB, реализующий порт OrderRepository
    import { MongoClient, Collection } from 'mongodb';
    import { OrderRepository, Order } from '../core/domain';
    
    class MongoOrderRepository implements OrderRepository {
        private orders: Collection;
    
        constructor(client: MongoClient) {
            this.orders = client.db('shop').collection('orders');
        }
    
        async save(order: Order): Promise<void> {
            // Адаптер преобразует вызов save() в запрос к MongoDB
            await this.orders.insertOne(this.toPersistence(order));
        }
    
        private toPersistence(order: Order) { /*...*/ }
        private toDomain(doc: any) { /*...*/ }
    }
    

Почему это название точнее, чем «гексагональная»?

  1. Описывает суть, а не форму: «Гексагональная» — лишь визуальная метафора (ядро в центре, адаптеры на сторонах шестиугольника, символизирующего «разные стороны» подключения). Шестиугольник выбран, чтобы избежать ассоциаций с «верхом» и «низом» (как в слоистой архитектуре). Количество сторон не имеет значения. Название «порты и адаптеры» прямо указывает на архитектурные паттерны, которые используются.

  2. Подчеркивает механизм инверсии зависимостей (Dependency Inversion Principle): Зависимости направлены внутрь, к ядру. Внешние слои (адаптеры) зависят от интерфейсов ядра (портов), а не наоборот. Это ключевой принцип, который обеспечивает тестируемость (ядро можно тестировать с mock-адаптерами) и сменяемость технологий (чтобы сменить БД, нужно лишь написать новый адаптер, не трогая ядро).

  3. Аналогия с компьютерными портами: Порт USB на ноутбуке — это четкий интерфейс (контракт на напряжение, форму, протокол). К нему можно подключить мышь, флешку или принтер через соответствующие адаптеры (сами устройства). Аналогично, порт в архитектуре — это контракт, а адаптер — его реализация для конкретной «периферии».

Преимущества подхода «Портов и Адаптеров»

  • Фокус на бизнес-логике: Разработка ядра ведется в изоляции от инфраструктурных проблем.
  • Гибкость и сопровождаемость: Замена внешних компонентов (миграция с REST на GraphQL, с MongoDB на PostgreSQL) требует минимальных изменений.
  • Тестируемость: Ядро можно покрыть юнит-тестами, подменяя адаптеры на заглушки (stubs/mocks).
  • Параллельная разработка: Команды могут работать над ядром и адаптерами относительно независимо.

Таким образом, «Архитектура портов и адаптеров» — это не просто альтернативное название, а семантически точное описание её фундаментального принципа: ядро системы объявляет порты (интерфейсы), а всё внешнее подключается к ним через адаптеры (реализации). Это делает систему гибкой, устойчивой к изменениям во внешнем мире и сфокусированной на основной ценности — бизнес-логике.