Ответ
В гексагональной архитектуре (также известной как Ports and Adapters) порт — это интерфейс, который определяет контракт взаимодействия между ядром приложения (бизнес-логикой) и внешним миром (адаптерами). Порты служат точками входа и выхода для ядра, абстрагируя детали реализации и позволяя ядру оставаться полностью независимым от внешних систем (баз данных, UI, сторонних API и т.д.). Это обеспечивает высокую степень изоляции, облегчает тестирование и замену внешних зависимостей.
Порты бывают двух основных типов:
- Входные (Driving/Primary Ports): Определяют, как внешние клиенты (например, UI, REST API, CLI) взаимодействуют с ядром приложения. Они инициируют действия в бизнес-логике.
- Выходные (Driven/Secondary Ports): Определяют, как ядро приложения взаимодействует с внешними сервисами (например, базами данных, файловой системой, внешними API). Ядро вызывает эти порты для выполнения операций во внешнем мире.
Пример выходного порта для работы с хранилищем пользователей (Python):
from abc import ABC, abstractmethod
# Это порт — абстрактный интерфейс, который ядро приложения будет использовать
class UserRepository(ABC):
@abstractmethod
def save(self, user: 'User') -> None:
"""Сохраняет пользователя в хранилище."""
pass
@abstractmethod
def get_by_id(self, user_id: str) -> 'User':
"""Получает пользователя по ID из хранилища."""
pass
Пояснение: Ядро приложения будет зависеть только от этого абстрактного UserRepository. Конкретные реализации (например, PostgresUserRepository, InMemoryUserRepository) будут являться адаптерами, которые реализуют этот порт, подключая ядро к реальным внешним системам. Это позволяет легко менять технологию хранения данных без изменения бизнес-логики.
Ответ 18+ 🔞
Давай разжую тебе эту умную архитектуру, как будто мы на кухне сидим и я тебе объясняю, почему твой код — говно, а мой — нет.
Смотри, есть у тебя приложение. Внутри него — святое святых, ядро. Это твоя бизнес-логика, мозги проекта. И вот этот мозг должен жить в стерильной камере, в полной изоляции от всего внешнего, пиздецового мира. От этих твоих баз данных, которые падают, от апишек, которые меняют контракты, от интерфейсов, которые каждый месяц перерисовывают.
Так вот, чтобы мозг не заразился всей этой внешней хуйнёй, мы строим шлюзы. Это и есть порты. По сути, это просто интерфейсы, правила на бумаге. Они кричат миру: «Хочешь со мной общаться — делай ТАК и ТОЛЬКО ТАК!».
Порты бывают двух сортов, как гости на вечеринке:
- Входные (Driving Ports). Это те, кто приходит и начинает движ. Кричит: «Эй, ядро! Сделай что-нибудь!». Это твой UI, твой REST-контроллер, CLI-команда. Они пинают ядро, чтобы оно начало работать.
- Выходные (Driven Ports). А это — те, кого ядро само дёргает за штаны, когда ему что-то нужно снаружи. Например: «Эй, хранилище! Сохрани-ка мне эту хуйню!» или «Эй, почтовый сервис! Отправь письмо этому челу!». Ядро не знает, КАК это будет сделано. Оно знает только ЧТО нужно сделать.
Вот смотри, живой пример. Допустим, ядру нужно работать с пользователями. Оно не будет, блядь, вставлять в себя SQL-запросы. Оно скажет: «Дайте мне интерфейс UserRepository с методами save и get_by_id». И всё.
from abc import ABC, abstractmethod
# Вот он, порт! Чистейшая абстракция, ебушки-воробушки!
class UserRepository(ABC):
@abstractmethod
def save(self, user: 'User') -> None:
"""Сохраняет пользователя в хранилище."""
pass
@abstractmethod
def get_by_id(self, user_id: str) -> 'User':
"""Получает пользователя по ID из хранилища."""
pass
А теперь магия, ёпта! Твоё ядро, эта недотрога, будет зависеть только от этого интерфейса. А кто будет делать реальную работу?
Адаптеры! Это уже конкретные ребята с грязными руками.
Хочешь в Postgres? Пожалуйста:
class PostgresUserRepository(UserRepository):
def save(self, user):
# Тут твой SQL, connection pool и прочий ад
pass
Пишешь тесты и не хочешь городить огород? Да похуй!
class InMemoryUserRepository(UserRepository):
def __init__(self):
self._users = {} # Просто словарь, ёбта!
def save(self, user):
self._users[user.id] = user
def get_by_id(self, user_id):
return self._users.get(user_id)
И твоё ядро, блядь, вообще не заметит подмены. Оно как тот самый Герасим из рассказа: ему сказали «Муму» — оно и радо. Ему похуй, жива собака или утоплена. Его дело — тыкать в порт.
В этом и весь цимес, чувак. Мозги приложения остаются чистыми, их можно тестировать в полной изоляции, подсовывая им заглушки. А всю внешнюю хуйню — базы, апи, файлы — можно менять, не трогая саму логику. Просто пишешь новый адаптер, который подходит под старый порт, и впендюриваешь его в приложение.
Красота, да и только. А не то что у тебя — бизнес-правила, перемешанные с SQL-джойнами, а потом ещё и HTML-теги сверху. Пиздец, а не архитектура.