Hexagonal Architecture (Ports and Adapters)
Гексагональная архитектура (Алистер Кокберн) — реинкарнация слоистой/чистой архитектуры с чётко определёнными портами и адаптерами. Бизнес-логика в центре, всё остальное снаружи.
Что это / Зачем
- Ядро = бизнес-логика (домен + application).
- Порт — интерфейс, через который ядро общается с внешним миром.
- Адаптер — конкретная реализация порта (Postgres-адаптер, HTTP-адаптер, Stripe-адаптер).
- Можно подменять адаптеры без правки ядра: Mongo вместо Postgres, gRPC вместо HTTP.
Driving vs Driven адаптеры
- Driving (primary) — нас вызывают: HTTP-сервер, GraphQL, CLI, очередь команд.
- Driven (secondary) — мы вызываем: БД, внешние API, файловое хранилище.
[HTTP] ──┐ ┌── [Postgres]
[CLI] ──┼──> [Port In] [DOMAIN] [Port Out] ──── [Redis]
[Queue]──┘ └── [Stripe]
driving driven
Признаки правильной реализации
- Доменный код не импортирует Express, Postgres, axios — только интерфейсы.
- Каждая внешняя зависимость спрятана за портом.
- Тесты домена работают с in-memory адаптерами, без сети.
Пример
// порт (интерфейс) — в домене
interface UserRepo {
findById(id: number): Promise<User>;
}
// driven адаптер — в инфраструктуре
class PgUserRepo implements UserRepo {
async findById(id: number) { return this.db.query(…); }
}
// driving адаптер
class HttpController {
constructor(private getUser: GetUserUseCase) {}
async get(req, res) { res.json(await this.getUser.exec(req.params.id)); }
}
Антипаттерн
// доменный сервис импортирует Express и Postgres → не гексагональная
import { Request } from 'express';
import { Pool } from 'pg';
class UserService {
async create(req: Request) { /* … */ }
}
🎓 Источники
- 🎓 [Public Interview #7 — Hexagonal Architecture] · 2022-05-13 · YouTube
- «Гексагональная архитектура — реинкарнация слоёной/чистой архитектуры, в которой чётко определены порты и адаптеры».
- Характерна Inversion of Control, а не наследование. Mongo vs Postgres — через единый порт.
- Гексагональная не сложнее чистой — она понятнее: даёт готовые шаблоны для частей приложения.