Clean Architecture: обзор

Clean Architecture — архитектурная концепция Роберта Мартина, разделяющая систему на концентрические слои с правилом зависимости: внутренние слои не зависят от внешних.

Зачем нужно

Clean Architecture решает главную проблему долгосрочных проектов — «прилипание» бизнес-логики к фреймворку, БД или UI. Когда логика независима, её можно тестировать в изоляции, менять React на Vue или PostgreSQL на MongoDB без переписывания ядра. Архитектура описывает не конкретный фреймворк, а принципы организации зависимостей.

Где используется

  • Крупные SPA-приложения, где бизнес-логика сложная и критичная
  • Серверные Node.js-приложения с NestJS или чистым Express
  • Проекты с высокими требованиями к тестируемости (unit тесты без браузера/БД)
  • Команды, где несколько разработчиков параллельно работают над разными слоями
  • Долгосрочные продукты, где предполагается смена технологий

Основной контент

Слои Clean Architecture (снаружи → внутрь)

┌─────────────────────────────────────┐
│  Frameworks & Drivers               │ ← React, Express, Prisma
│  ┌─────────────────────────────┐    │
│  │  Interface Adapters         │    │ ← Controllers, Presenters, Gateways
│  │  ┌───────────────────────┐  │    │
│  │  │  Use Cases            │  │    │ ← Бизнес-правила приложения
│  │  │  ┌─────────────────┐  │  │    │
│  │  │  │  Entities       │  │  │    │ ← Бизнес-правила предметной области
│  │  │  └─────────────────┘  │  │    │
│  │  └───────────────────────┘  │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

Правило зависимости: зависимости направлены только внутрь. Entities ничего не знают об Use Cases, Use Cases — о Controllers, Controllers — о React.

Пример структуры проекта

src/
  domain/              ← Entities
    entities/
      User.ts
      Order.ts
    repositories/      ← интерфейсы (abstractions)
      IUserRepository.ts
  
  application/         ← Use Cases
    use-cases/
      CreateUser.ts
      GetUserById.ts
  
  infrastructure/      ← конкретные реализации
    repositories/
      PrismaUserRepository.ts
    http/
      express/
        UserController.ts
  
  presentation/        ← UI
    components/
      UserCard.tsx

Пример Use Case (без зависимости от фреймворка)

// domain/repositories/IUserRepository.js — интерфейс (контракт)
// { findById(id), save(user), findByEmail(email) }

// application/use-cases/CreateUser.js
class CreateUserUseCase {
  constructor(userRepository, emailService) {
    this.userRepository = userRepository;
    this.emailService = emailService;
  }

  async execute({ name, email, password }) {
    // Проверка дубликата — бизнес-правило
    const existing = await this.userRepository.findByEmail(email);
    if (existing) {
      throw new Error('Пользователь с таким email уже существует');
    }

    const user = { id: crypto.randomUUID, name, email, createdAt: new Date };
    await this.userRepository.save(user);
    await this.emailService.sendWelcome(email, name);

    return user;
  }
}
// Use Case не знает про Express, React, Prisma, Postgres
// Тест: передать mock-репозиторий и mock-emailService

// infrastructure/repositories/PrismaUserRepository.js
class PrismaUserRepository {
  constructor(prisma) { this.prisma = prisma; }
  async findByEmail(email) { return this.prisma.user.findUnique({ where: { email } }); }
  async save(user) { return this.prisma.user.create({ data: user }); }
}

// infrastructure/http/UserController.js (Express)
class UserController {
  constructor(createUserUseCase) { this.createUser = createUserUseCase; }
  async post(req, res) {
    try {
      const user = await this.createUser.execute(req.body);
      res.status(201).json(user);
    } catch (err) {
      res.status(400).json({ error: err.message });
    }
  }
}

Частые ошибки

  • Правило зависимости нарушено внутрь: Use Case импортирует Prisma-модель напрямую — теперь смена ORM требует переписывания Use Cases.
  • Слои смешаны: бизнес-логика в Controller или SQL в Entity — самая частая проблема на практике.
  • Overkill для малых проектов: для CRUD-приложения с 5 сущностями Clean Architecture добавляет лишний boilerplate без реальной пользы. Применяйте YAGNI.

🎓 Источники

  • 🎓 [Public Interview #7 — Hexagonal/Clean] · 2022-05-13 · YouTube
    • «Гексагональная — реинкарнация слоёной/чистой архитектуры с чёткими портами и адаптерами».
    • Характерна Inversion of Control, не наследование.
  • 🎓 [Дядя Боб и розовые пони] и автор · 2025-02-18 · YouTube
    • Критика догматичного следования Clean Architecture/Clean Code.
  • 🎓 [Архитектурные принципы Metarhia] · 2023-12-06 · YouTube
    • Альтернативная позиция: «В архитектуре нет единственно правильного решения. Структура приложения важнее архитектуры».

Связанные темы

Ресурсы