Ответ
Гексагональная архитектура (Ports & Adapters) предполагает разделение кода на три слоя:
- Домен (ядро) - бизнес-логика без зависимостей от внешнего мира.
- Порты - интерфейсы для взаимодействия с внешними системами (API, БД и т.д.).
- Адаптеры - реализация портов для конкретных технологий.
Пример на 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. Суть в том, чтобы не превратить код в одну большую свалку, где всё перемешано, как говно с макаронами.
Вот смотри, тут всё по полочкам, блядь:
- Домен (или ядро) — это святое, ёпта! Твоя бизнес-логика, правила, сущности. Оно должно быть чистым, как слеза младенца, и нихуя не знать про базы данных, HTTP-запросы и прочую внешнюю хуйню. Живёт в своём мире, блядь.
- Порты — это, типа, договор. Интерфейсы, объявляющие: "А вот так вот, сука, со мной можно общаться". Хочешь сохранить пользователя? Вот тебе интерфейс
UserRepository. А как именно — это уже твои проблемы, ядру похуй. - Адаптеры — это уже конкретные поделки, которые этот договор выполняют. Нужно в 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. И тестируй свою логику в полной изоляции, блядь! Никаких поднятий баз, никаких сетей. Чистота, ебать!
Вот так вот, сука. Кажется сложно, но когда вьебешься — обратной дороги нет. Код становится управляемым, как послушная сука.