Какие абстракции предполагает гексагональная архитектура

Ответ

Гексагональная архитектура (Ports & Adapters) предполагает разделение кода на три слоя:

  1. Домен (ядро) - бизнес-логика без зависимостей от внешнего мира.
  2. Порты - интерфейсы для взаимодействия с внешними системами (API, БД и т.д.).
  3. Адаптеры - реализация портов для конкретных технологий.

Пример на TypeScript:

// Порт (интерфейс)
interface UserRepository {
  save(user: User): Promise<void>;
}

// Адаптер для MongoDB
class MongoUserRepository implements UserRepository {
  async save(user: User) {
    // Реализация для MongoDB
  }
}

// Домен использует только порт
class UserService {
  constructor(private repo: UserRepository) {}

  async register(user: User) {
    // Бизнес-логика
    await this.repo.save(user);
  }
}

Ключевые принципы:

  • Зависимости направлены к ядру
  • Внешние системы подключаются через адаптеры
  • Легко тестировать (можно подменить адаптеры моками)

Ответ 18+ 🔞

Да ты посмотри, какая хуйня придумана, блядь! Гексагональная архитектура, или, как её ещё обзывают — Ports & Adapters. Суть в том, чтобы не превратить код в одну большую свалку, где всё перемешано, как говно с макаронами.

Вот смотри, тут всё по полочкам, блядь:

  1. Домен (или ядро) — это святое, ёпта! Твоя бизнес-логика, правила, сущности. Оно должно быть чистым, как слеза младенца, и нихуя не знать про базы данных, HTTP-запросы и прочую внешнюю хуйню. Живёт в своём мире, блядь.
  2. Порты — это, типа, договор. Интерфейсы, объявляющие: "А вот так вот, сука, со мной можно общаться". Хочешь сохранить пользователя? Вот тебе интерфейс UserRepository. А как именно — это уже твои проблемы, ядру похуй.
  3. Адаптеры — это уже конкретные поделки, которые этот договор выполняют. Нужно в MongoDB? Вот тебе адаптер для MongoDB. Завтра начальник захотел PostgreSQL? Пишешь новый адаптер, а ядро даже бровью не поведёт, потому что оно общается только через порт, блядь!

Вот, смотри на примере, чтобы совсем пиздец стало понятно:

// Это порт, договор, ёпта! Просто говорит: "Умею сохранять юзера".
interface UserRepository {
  save(user: User): Promise<void>;
}

// А это уже адаптер, конкретная поделка для MongoDB.
class MongoUserRepository implements UserRepository {
  async save(user: User) {
    // А вот тут уже реальная, грязная работа с базой.
    // Ядро про эту хуйню даже не догадывается!
  }
}

// А это уже наше ядро, святая святых! Оно принимает в конструктор НЕ КОНКРЕТНУЮ МОНГО, а АБСТРАКТНЫЙ ПОРТ.
class UserService {
  constructor(private repo: UserRepository) {} // Смотри сюда! Тип — интерфейс!

  async register(user: User) {
    // Какая-то важная бизнес-логика, проверки, хуйня.
    // И тут оно говорит: "Эй, репозиторий, сохрани этого юзера!"
    // А кто там — Mongo, Postgres или файлик на диске — ядру похуй абсолютно!
    await this.repo.save(user);
  }
}

И главные фишки этой архитектуры, блядь:

  • Зависимости идут ОТ периферии К ядру, а не наоборот. Внешний мир зависит от правил ядра, а ядро не зависит ни от кого. Красота, ёпта!
  • Вся внешняя хуйня (базы, апишки, смс-отправлялки) цепляется через эти самые адаптеры. Захотел поменять — вырвал один адаптер, воткнул другой. Ядро даже не чихнуло.
  • Тестировать — одно ебаное удовольствие! Хочешь протестировать UserService? Подсовываешь ему в конструктор не реальную базу, а хуёвую заглушку (mock), которая реализует тот же интерфейс UserRepository. И тестируй свою логику в полной изоляции, блядь! Никаких поднятий баз, никаких сетей. Чистота, ебать!

Вот так вот, сука. Кажется сложно, но когда вьебешься — обратной дороги нет. Код становится управляемым, как послушная сука.