Ответ
Чистая архитектура (Clean Architecture) — это подход к проектированию программного обеспечения, предложенный Робертом Мартином (Uncle Bob), который фокусируется на создании систем, независимых от фреймворков, баз данных, UI и внешних сервисов. Ее основная цель — обеспечить долгосрочную поддерживаемость, тестируемость и гибкость приложения, защищая бизнес-логику от изменений во внешних слоях.
Основные принципы:
- Независимость от фреймворков: Система не должна зависеть от наличия какой-либо библиотеки или фреймворка.
- Тестируемость: Бизнес-правила могут быть протестированы без UI, базы данных, веб-сервера или других внешних элементов.
- Независимость от UI: UI может быть легко изменен без изменения остальной части системы.
- Независимость от базы данных: Можно легко поменять базу данных (SQL, NoSQL) без изменения бизнес-логики.
- Независимость от внешних агентов: Бизнес-логика не должна знать о внешних API или сервисах.
Эти принципы реализуются через правило зависимостей: зависимости должны быть направлены внутрь, от внешних слоев к внутренним.
Слои Чистой архитектуры (от внешнего к внутреннему):
- Frameworks & Drivers (Инфраструктура): Веб-фреймворки (FastAPI, Django), базы данных, UI, внешние API.
- Interface Adapters (Адаптеры интерфейсов): Преобразуют данные из внешних слоев в формат, понятный Use Cases и Entities, и наоборот (контроллеры, презентеры, репозитории).
- Use Cases (Варианты использования): Содержат специфичную для приложения бизнес-логику. Оркестрируют поток данных между Entities и Interface Adapters.
- Entities (Сущности): Содержат общие для всего предприятия бизнес-правила и данные. Это ядро приложения.
Пример на Python:
from abc import ABC, abstractmethod
from typing import List, Optional
# 1. Entities (Сущности) - Общие бизнес-правила
class User:
def __init__(self, user_id: int, name: str, email: str):
self.user_id = user_id
self.name = name
self.email = email
def is_valid(self) -> bool:
return bool(self.name and "@" in self.email)
# 2. Use Cases (Варианты использования) - Специфичная бизнес-логика
class UserInputPort(ABC): # Входящий порт для Use Case
@abstractmethod
def create_user(self, name: str, email: str) -> None: ...
@abstractmethod
def get_user_by_id(self, user_id: int) -> None: ...
class UserOutputPort(ABC): # Исходящий порт для Use Case (презентер)
@abstractmethod
def present_user(self, user: User) -> None: ...
@abstractmethod
def present_error(self, message: str) -> None: ...
class UserRepository(ABC): # Исходящий порт для Use Case (репозиторий)
@abstractmethod
def save(self, user: User) -> None: ...
@abstractmethod
def get_by_id(self, user_id: int) -> Optional[User]: ...
class CreateUserUseCase(UserInputPort):
def __init__(self, user_repo: UserRepository, user_presenter: UserOutputPort):
self.user_repo = user_repo
self.user_presenter = user_presenter
def create_user(self, name: str, email: str):
user = User(user_id=0, name=name, email=email) # ID будет присвоен репозиторием
if not user.is_valid():
self.user_presenter.present_error("Некорректные данные пользователя.")
return
self.user_repo.save(user)
self.user_presenter.present_user(user)
def get_user_by_id(self, user_id: int) -> None:
# Этот Use Case не предназначен для получения, но для примера
pass
# 3. Interface Adapters (Адаптеры интерфейсов) - Реализации портов
class InMemoryUserRepository(UserRepository):
_users: List[User] = []
_next_id = 1
def save(self, user: User):
user.user_id = self._next_id
self._next_id += 1
self._users.append(user)
def get_by_id(self, user_id: int) -> Optional[User]:
return next((u for u in self._users if u.user_id == user_id), None)
class ConsoleUserPresenter(UserOutputPort):
def present_user(self, user: User):
print(f"Пользователь создан: ID={user.user_id}, Имя={user.name}, Email={user.email}")
def present_error(self, message: str):
print(f"Ошибка: {message}")
# 4. Frameworks & Drivers (Инфраструктура) - Точка входа
# Это может быть FastAPI, Flask, CLI и т.д.
# Пример использования:
user_repo = InMemoryUserRepository()
user_presenter = ConsoleUserPresenter()
create_user_interactor = CreateUserUseCase(user_repo, user_presenter)
create_user_interactor.create_user("John Doe", "john.doe@example.com")
create_user_interactor.create_user("Jane Smith", "jane.smith@example.com")
create_user_interactor.create_user("Invalid User", "invalid") # Пример ошибки
Преимущества:
- Высокая тестируемость: Бизнес-логика (Use Cases) полностью изолирована и может быть протестирована без зависимостей от БД или UI.
- Гибкость и масштабируемость: Легко заменять внешние компоненты (БД, UI, фреймворки) без изменения ядра приложения.
- Долговечность: Бизнес-правила, являющиеся наиболее стабильной частью системы, находятся в центре и защищены от внешних изменений.
Недостатки:
- Сложность для малых проектов: Введение множества абстракций и слоев может быть избыточным для простых приложений.
- Больше boilerplate-кода: Требует написания большего количества кода для связующих элементов (адаптеров, портов).
- Крутая кривая обучения: Требует глубокого понимания принципов ООП и архитектуры.
Чистая архитектура наиболее эффективна для крупных, долгосрочных проектов, где важна стабильность бизнес-логики и возможность адаптации к меняющимся технологиям.