← Назад к вопросам

Какие плюсы и минусы чистой архитектуры?

2.4 Senior🔥 221 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Clean Architecture: Принципы, Плюсы и Минусы

Clean Architecture (чистая архитектура), предложенная Робертом Мартином, — это подход к организации кода, где бизнес-логика полностью независима от деталей реализации (БД, фреймворки, UI).

Структура Clean Architecture

┌─────────────────────────────────────────────┐
│         Presentation Layer (UI)              │ Фреймворк зависит от здесь
│      (Controllers, Handlers, Views)         │ всего остального
├─────────────────────────────────────────────┤
│      Application Layer (Use Cases)          │ Зависимости только внутрь
│  (Business Logic Orchestration, DTOs)       │
├─────────────────────────────────────────────┤
│       Domain Layer (Business Rules)         │
│    (Entities, Value Objects, Interfaces)    │ Не зависит ни от чего
├─────────────────────────────────────────────┤
│     Infrastructure Layer (Implementation)    │ Реализация интерфейсов
│    (Database, External APIs, Email)         │ из Domain
└─────────────────────────────────────────────┘

Правило зависимостей: → → → (только внутрь, не наружу)

Пример на Python:

# domain/entities.py — не зависит ни от чего
from dataclasses import dataclass
from abc import ABC

@dataclass
class User:
    id: str
    name: str
    email: str
    
    def validate(self):
        if not self.email or '@' not in self.email:
            raise ValueError("Invalid email")

class UserRepository(ABC):
    async def save(self, user: User) -> None: ...
    async def find_by_id(self, user_id: str) -> User: ...

# application/use_cases.py — зависит только от domain
from domain.entities import User, UserRepository

class CreateUserUseCase:
    def __init__(self, user_repo: UserRepository):
        self.user_repo = user_repo
    
    async def execute(self, name: str, email: str) -> User:
        user = User(id=uuid4(), name=name, email=email)
        user.validate()
        await self.user_repo.save(user)
        return user

# infrastructure/repositories.py — реализует интерфейсы domain
from sqlalchemy.orm import Session
from domain.entities import User, UserRepository
from infrastructure.models import UserModel

class SQLAlchemyUserRepository(UserRepository):
    def __init__(self, db: Session):
        self.db = db
    
    async def save(self, user: User) -> None:
        model = UserModel(id=user.id, name=user.name, email=user.email)
        self.db.add(model)
        await self.db.commit()
    
    async def find_by_id(self, user_id: str) -> User:
        model = await self.db.query(UserModel).filter(
            UserModel.id == user_id
        ).first()
        return User(id=model.id, name=model.name, email=model.email)

# presentation/handlers.py — зависит от application
from fastapi import APIRouter
from application.use_cases import CreateUserUseCase

router = APIRouter()

@router.post("/users")
async def create_user(name: str, email: str, use_case: CreateUserUseCase):
    user = await use_case.execute(name, email)
    return {"id": user.id, "name": user.name}

Плюсы Clean Architecture

1. Независимость от фреймворков

Бизнес-логика не зависит от Django, FastAPI, Flask и т.д.:

# Domain code работает везде, в любом фреймворке
class CalculateUserScore:
    def execute(self, user: User) -> int:
        # Чистая логика, без HTTP, БД или фреймворка
        return user.posts_count * 10 + user.comments_count * 5

# Легко мигрировать между фреймворками
# FastAPI версия
from fastapi import FastAPI
app = FastAPI()

@app.get("/users/{user_id}/score")
async def get_user_score(user_id: str, use_case: CalculateUserScore):
    return use_case.execute(user)

# Flask версия (та же логика)
from flask import Flask
app = Flask(__name__)

@app.route("/users/<user_id>/score")
def get_user_score(user_id: str):
    return use_case.execute(user)

2. Простота тестирования

Можно тестировать бизнес-логику без БД, HTTP, файловой системы:

# Тест не нужна реальная БД
class MockUserRepository(UserRepository):
    def __init__(self):
        self.saved_users = []
    
    async def save(self, user: User) -> None:
        self.saved_users.append(user)
    
    async def find_by_id(self, user_id: str) -> User:
        return next(u for u in self.saved_users if u.id == user_id)

async def test_create_user():
    repo = MockUserRepository()
    use_case = CreateUserUseCase(repo)
    
    user = await use_case.execute("Alice", "alice@example.com")
    
    assert user.name == "Alice"
    assert len(repo.saved_users) == 1
    # Тест быстрый, не нужна БД, не нужен HTTP!

3. Легко менять детали реализации

Можно менять БД с PostgreSQL на MongoDB без изменения логики:

# Интерфейс остается прежним
class UserRepository(ABC):
    async def save(self, user: User) -> None: ...

# PostgreSQL реализация
class PostgreSQLUserRepository(UserRepository):
    async def save(self, user: User) -> None:
        # SQL...
        pass

# MongoDB реализация
class MongoDBUserRepository(UserRepository):
    async def save(self, user: User) -> None:
        # MongoDB...
        pass

# Логика не меняется!
class CreateUserUseCase:
    def __init__(self, user_repo: UserRepository):
        # Не важно какая реализация
        self.user_repo = user_repo

4. Предсказуемость и надежность

Ясная структура упрощает понимание:

Проблема в коде? → Смотри в domain (бизнес-правила)
Проблема в HTTP? → Смотри в presentation
Проблема с БД? → Смотри в infrastructure
Проблема в обработке? → Смотри в application

5. Масштабируемость

Легко добавлять новые функции и интеграции:

# Была функция GetUser
class GetUserUseCase:
    async def execute(self, user_id: str) -> User: ...

# Добавляем GetUserWithPosts без изменения существующего кода
class GetUserWithPostsUseCase:
    def __init__(self, user_repo: UserRepository, post_repo: PostRepository):
        self.user_repo = user_repo
        self.post_repo = post_repo
    
    async def execute(self, user_id: str) -> dict:
        user = await self.user_repo.find_by_id(user_id)
        posts = await self.post_repo.find_by_user(user_id)
        return {"user": user, "posts": posts}

Минусы Clean Architecture

1. Оверинжиниринг для малых проектов

Для простого скрипта создание 4 слоев избыточно:

# Простой CRUD — оверкомплицировано
# domain/user.py
class User:
    def __init__(self, id, name): ...

# application/use_cases.py
class CreateUserUseCase: ...

# infrastructure/repositories.py
class UserRepository: ...

# presentation/handlers.py
@router.post("/users")
async def create_user(): ...

# Вместо простого
@router.post("/users")
async def create_user(db: Session, name: str):
    user = User(name=name)
    db.add(user)
    db.commit()
    return user

2. Много boilerplate кода и абстракций

# Много интерфейсов и классов для простой функции

# Интерфейс
class NotificationService(ABC):
    @abstractmethod
    async def send(self, message: str) -> None: ...

# Реализация 1
class EmailNotificationService(NotificationService):
    async def send(self, message: str) -> None:
        # email...
        pass

# Реализация 2
class SMSNotificationService(NotificationService):
    async def send(self, message: str) -> None:
        # sms...
        pass

# Use Case
class NotifyUserUseCase:
    def __init__(self, notification_service: NotificationService):
        self.service = notification_service
    
    async def execute(self, user_id: str) -> None:
        await self.service.send("Hello")

# Для простого уведомления!

3. Производительность: много слоев = больше вызовов

# Простой запрос проходит через 4 слоя

# Слой 1: Presentation
@router.get("/users/{user_id}")
async def get_user(user_id: str, get_user_use_case: GetUserUseCase):
    return await get_user_use_case.execute(user_id)

# Слой 2: Application
class GetUserUseCase:
    async def execute(self, user_id: str):
        return await self.repository.find_by_id(user_id)

# Слой 3: Infrastructure
class UserRepository:
    async def find_by_id(self, user_id: str):
        return await self.db.query(...)

# Слой 4: Database
# Запрос

# Для простого SELECT может быть избыточно

4. Сложность отладки через много слоев

# Ошибка где-то глубоко
# Нужно пройти через все слои для отладки

@router.post("/users")
async def create_user(data, use_case):
    # 1. Presentation layer
    return await use_case.execute(data)

class CreateUserUseCase:
    async def execute(self, data):
        # 2. Application layer
        user = User(**data)
        return await self.repo.save(user)

class UserRepository:
    async def save(self, user):
        # 3. Infrastructure layer
        model = UserModel(**user.dict())
        self.db.add(model)
        await self.db.commit()
        # 4. Database layer
        return model

5. Типизация может быть излишней

# Много интерфейсов может замедлить разработку

class PaymentProcessor(ABC):
    @abstractmethod
    async def process_payment(self, amount: float) -> dict: ...

class StripePaymentProcessor(PaymentProcessor):
    async def process_payment(self, amount: float) -> dict:
        # реальная логика
        pass

class MockPaymentProcessor(PaymentProcessor):
    async def process_payment(self, amount: float) -> dict:
        # для тестов
        pass

# Для простого payment это может быть overengineering

Когда использовать Clean Architecture

Идеально подходит для:

# 1. Долгосрочные проекты
# - Проект будет развиваться несколько лет
# - Много разработчиков
# - Частые изменения требований

# 2. Сложная бизнес-логика
# - Много use cases
# - Сложные правила
# - Много интеграций

# 3. Когда нужна тестируемость
# - Требуется 90%+ coverage
# - Дорогой code review
# - Production system

Избегай для:

# 1. MVP и прототипы
# - Быстрое создание
# - Неясные требования
# - Может измениться полностью

# 2. Простые скрипты
# - Один модуль
# - Простая логика
# - Не требует расширения

# 3. CRUD приложения
# - Простые операции
# - Минимальная бизнес-логика

Практический баланс

# Гибридный подход: только нужные слои

class Order:
    """Domain entity"""
    def __init__(self, user_id: str, items: list):
        self.user_id = user_id
        self.items = items
    
    def validate(self):
        if not self.items:
            raise ValueError("Order must have items")

class OrderRepository:
    """Infrastructure"""
    async def save(self, order: Order) -> None:
        db.add(OrderModel(**order.dict()))
        await db.commit()

@router.post("/orders")
async def create_order(data: dict, repo: OrderRepository):
    """Presentation + Application in one"""
    order = Order(**data)
    order.validate()
    await repo.save(order)
    return {"id": order.id}

# Не излишне, но не лишено структуры

Итог

АспектClean Architecture
ТестируемостьОтлично
ПоддерживаемостьОтлично
ПроизводительностьМожет быть медленнее
Скорость разработкиМедленнее изначально
МасштабируемостьОтлично
ПростотаСложнее
Best forEnterprise, долгосрочные проекты
Avoid forMVP, простые скрипты

Рекомендация: Используй Clean Architecture в сочетании с YAGNI (You Ain't Gonna Need It) — добавляй абстракции только когда они действительно нужны, а не "на будущее".