PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    Clean Architecture: чистая архитектура для веб-разработки

    Clean Architecture: чистая архитектура для веб-разработки

    Аватар автора Clean Architecture: чистая архитектура для веб-разработки

    Антон Ларичев

    Иконка календаря02 июля 2026
    архитектураclean architecturetypescriptbackendnode.jsпроектированиеmiddleИконка уровня middle
    Картинка поста Clean Architecture: чистая архитектура для веб-разработки

    Введение

    Чистая архитектура (Clean Architecture) — это набор принципов проектирования, предложенный Робертом Мартином в 2012 году. Её цель — создать систему, в которой бизнес-логика не зависит от фреймворков, баз данных и внешних сервисов. Для веб-разработчика это означает возможность поменять Express на Fastify, PostgreSQL на MongoDB или REST на GraphQL без переписывания ядра приложения.

    В статье разберём основные концепции, реализуем пример на TypeScript и рассмотрим типичные ошибки при внедрении.

    Основные принципы и слои

    Clean Architecture строится вокруг одного ключевого правила — правила зависимостей: зависимости в коде могут указывать только внутрь, к более высокоуровневым политикам. Архитектура разделена на четыре концентрических слоя.

    Entities — сущности

    Самый внутренний слой. Содержит бизнес-объекты и правила, которые не зависят ни от чего внешнего. Это ядро приложения.

    export class User {
      private constructor(
        public readonly id: string,
        public readonly email: string,
        private readonly password: string
      ) {}
    
      // Фабричный метод защищает инварианты на уровне домена
      static create(id: string, email: string, password: string): User {
        if (!email.includes('@')) {
          throw new Error('Некорректный email');
        }
        if (password.length < 8) {
          throw new Error('Пароль слишком короткий');
        }
        return new User(id, email, password);
      }
    }
    

    Use Cases — сценарии использования

    Второй слой содержит логику приложения — конкретные действия, которые пользователь может совершить. Use Cases оркестрируют сущности и обращаются к внешнему миру через интерфейсы (порты).

    // Порт — интерфейс, который реализует внешний слой
    export interface UserRepository {
      save(user: User): Promise<void>;
      findByEmail(email: string): Promise<User | null>;
    }
    
    export class RegisterUserUseCase {
      constructor(private readonly userRepository: UserRepository) {}
    
      async execute(email: string, password: string): Promise<void> {
        // Проверяем уникальность email перед созданием
        const existing = await this.userRepository.findByEmail(email);
        if (existing) {
          throw new Error('Пользователь с таким email уже существует');
        }
        const user = User.create(crypto.randomUUID(), email, password);
        await this.userRepository.save(user);
      }
    }
    

    Interface Adapters — адаптеры интерфейса

    Третий слой преобразует данные между форматом Use Cases и внешним миром. Здесь живут контроллеры, презентеры и реализации репозиториев.

    // Реализация репозитория через Prisma — знает о БД, но не знает о HTTP
    export class PrismaUserRepository implements UserRepository {
      constructor(private readonly prisma: PrismaClient) {}
    
      async save(user: User): Promise<void> {
        await this.prisma.user.create({
          data: { id: user.id, email: user.email },
        });
      }
    
      async findByEmail(email: string): Promise<User | null> {
        const record = await this.prisma.user.findUnique({ where: { email } });
        if (!record) return null;
        // Восстанавливаем доменную сущность из записи БД
        return User.create(record.id, record.email, record.password);
      }
    }
    
    // HTTP-контроллер — адаптер между запросом и Use Case
    export class UserController {
      constructor(private readonly registerUser: RegisterUserUseCase) {}
    
      async register(req: Request, res: Response): Promise<void> {
        try {
          await this.registerUser.execute(req.body.email, req.body.password);
          res.status(201).json({ message: 'Пользователь создан' });
        } catch (error) {
          res.status(400).json({ error: (error as Error).message });
        }
      }
    }
    

    Frameworks and Drivers — фреймворки и драйверы

    Самый внешний слой. Здесь конфигурация Express, подключение к БД, настройка DI-контейнера. Этот слой знает обо всех остальных, но остальные о нём — нет.

    // Точка входа — ручная сборка зависимостей (Composition Root)
    const prisma = new PrismaClient();
    const userRepository = new PrismaUserRepository(prisma);
    const registerUserUseCase = new RegisterUserUseCase(userRepository);
    const userController = new UserController(registerUserUseCase);
    
    const app = express();
    app.use(express.json());
    app.post('/users/register', (req, res) => userController.register(req, res));
    app.listen(3000);
    

    Структура папок

    Хорошая структура папок отражает слои архитектуры, а не технические детали фреймворка:

    src/
    ├── domain/               # Entities
    │   └── user/
    │       └── user.entity.ts
    ├── application/          # Use Cases и порты
    │   └── user/
    │       ├── register-user.use-case.ts
    │       └── user.repository.ts
    ├── infrastructure/       # Адаптеры и фреймворки
    │   ├── database/
    │   │   └── prisma-user.repository.ts
    │   └── http/
    │       └── user.controller.ts
    └── main.ts               # Composition Root
    

    Папка domain не импортирует ничего из infrastructure — это главный признак того, что правило зависимостей соблюдается.

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

    Утечка зависимостей внутрь. Самая распространённая ошибка — импорт Prisma-моделей или Express-типов внутри Use Cases. Это нарушает правило зависимостей и делает бизнес-логику неотделимой от фреймворка.

    Анемичные сущности. Когда User — просто объект с полями, а вся логика вынесена в сервисы, вы теряете главное преимущество: защиту инвариантов на уровне домена. Сущность должна сама знать, в каком состоянии она является корректной.

    Избыточные абстракции. Создавать интерфейс для каждого класса ради следования паттерну — лишняя работа. Интерфейс нужен там, где есть реальная необходимость подменять реализацию: репозитории, почтовые сервисы, платёжные шлюзы.

    Один сервис на всё. Не нужно создавать UserService с двадцатью методами. Один Use Case — одна операция. Это упрощает тестирование и делает код читаемым.

    Игнорирование тестируемости. Главная практическая польза Clean Architecture — лёгкое модульное тестирование. Use Cases легко тестируются с мок-репозиториями без поднятия БД или HTTP-сервера. Если тесты не пишутся, половина ценности подхода теряется.

    Заключение

    Clean Architecture — это не жёсткий канон, а набор принципов, которые адаптируются под нужды проекта. Для небольших CRUD-приложений полная реализация со всеми слоями избыточна. Но для сложных доменов с развитой бизнес-логикой этот подход даёт ощутимые преимущества: независимость от фреймворков, простоту тестирования и долгосрочную поддерживаемость кода.

    Начните с малого: выделите Use Cases из контроллеров и определите интерфейсы репозиториев. Этих двух шагов уже достаточно, чтобы почувствовать разницу.

    Иконка глаза3

    Комментарии

    0

    Постройте личный план изучения HTML и CSS - полный курс по вёрстке с нуля до уровня Middle — бесплатно!

    HTML и CSS - полный курс по вёрстке с нуля — часть карты развития Frontend, Mobile

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    CSS Flexbox

    Антон Ларичев
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.9
    бесплатно
    Подробнее
    изображение курса

    Основы JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Продвинутый JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста Паттерны проектирования на TypeScript: SOLID с примерами
    Иконка аватараАнтон
    Иконка календаря13 июня 2026
    typescriptsolidпаттерны проектирования+ 2middleИконка уровня middle

    Паттерны проектирования на TypeScript: SOLID с примерами

    Паттерны проектирования на TypeScript и принципы SOLID: разбор каждого принципа с практическими примерами кода, типичными ошибками и рекомендациями.

    Иконка чипа0
    Иконка глаза379
    Иконка комментариев0
    Картинка поста HTTP методы GET POST PUT DELETE: разница и применение
    Иконка аватараАнтон
    Иконка календаря27 июня 2026
    HTTPRESTAPI+ 2juniorИконка уровня junior

    HTTP методы GET POST PUT DELETE: разница и применение

    HTTP методы GET, POST, PUT, DELETE — основа любого REST API. Разбираем, чем они отличаются, когда применять каждый и какие ошибки допускают новички.

    Иконка чипа0
    Иконка глаза227
    Иконка комментариев0
    Картинка поста SOLID принципы в JavaScript: разбор на примерах
    Иконка аватараАнтон
    Иконка календаря26 июня 2026
    JavaScriptSOLIDООП+ 3middleИконка уровня middle

    SOLID принципы в JavaScript: разбор на примерах

    SOLID принципы в JavaScript — разбираем каждый принцип на реальных примерах кода, чтобы писать чистую и расширяемую архитектуру.

    Иконка чипа0
    Иконка глаза96
    Иконка комментариев0
    Иконка чипа0