Ответ
Гексагональная архитектура (Hexagonal Architecture), также известная как «Порты и Адаптеры», — это архитектурный паттерн, главная цель которого — изолировать доменную (бизнес-) логику приложения от внешних зависимостей, таких как UI, базы данных, сторонние API и фреймворки.
Ключевые компоненты:
- Ядро (Core/Domain): Центральная часть приложения, содержащая чистую бизнес-логику. Она не имеет зависимостей от внешних технологий.
- Порты (Ports): Это интерфейсы (абстрактные классы в Python), которые определяют контракты для взаимодействия с ядром. Делятся на два типа:
- Входящие (Driving) порты: API, который ядро предоставляет внешнему миру (например, интерфейс сервиса для обработки запросов).
- Исходящие (Driven) порты: Интерфейсы, которые ядро использует для получения данных извне (например, интерфейс репозитория для работы с БД).
- Адаптеры (Adapters): Конкретные реализации портов, которые «переводят» запросы из внешнего мира на язык ядра и наоборот.
- Входящие адаптеры: REST-контроллеры, gRPC-серверы, консольные команды.
- Исходящие адаптеры: ORM-репозитории, клиенты для внешних API, файловые хранилища.
Пример на Python:
from abc import ABC, abstractmethod
# --- Исходящий Порт (Driven Port) ---
class UserRepository(ABC):
@abstractmethod
def save(self, user_name: str):
pass
# --- Исходящий Адаптер (Driven Adapter) ---
class PostgresUserRepository(UserRepository):
def save(self, user_name: str):
# Логика сохранения в PostgreSQL
print(f"Пользователь '{user_name}' сохранен в PostgreSQL.")
# --- Ядро Приложения (Core) ---
class UserService:
# Ядро зависит только от абстракции (порта), а не от конкретной реализации
def __init__(self, user_repo: UserRepository):
self._user_repo = user_repo
def register_user(self, user_name: str):
# Бизнес-логика
if len(user_name) < 3:
raise ValueError("Имя пользователя слишком короткое")
self._user_repo.save(user_name)
# --- Входящий Адаптер (Driving Adapter) - условный REST-контроллер ---
if __name__ == "__main__":
# В точке входа мы собираем зависимости: ядро и конкретный адаптер
db_adapter = PostgresUserRepository()
user_service = UserService(user_repo=db_adapter)
# Внешний мир (например, HTTP-запрос) вызывает сервис
user_service.register_user("Alice")
Преимущества:
- Высокая тестируемость: Ядро можно тестировать в полной изоляции, подменяя адаптеры моками.
- Заменяемость технологий: Можно легко заменить базу данных (например, с PostgreSQL на MongoDB), просто написав новый адаптер, не трогая бизнес-логику.
- Независимость от фреймворков: Бизнес-логика не привязана к конкретному веб-фреймворку или ORM.