Чистая архитектура: от теории к практике. Разбираем примеры, которые работают

Чистая архитектура: от теории к практике. Разбираем примеры, которые работают

Чистая архитектура — это не просто модный термин из мира разработки, а философия построения программных систем, которые живут долго и не превращаются в "легаси" через полгода после релиза. Если вы устали от спагетти-кода, где изменение кнопки ломает логику авторизации, и хотите создавать гибкие, тестируемые и независимые от фреймворков приложения — вы по адресу. В этой статье мы разберем не только принципы, но и конкретные, рабочие примеры чистой архитектуры на разных языках и платформах.

Что такое чистая архитектура? Суть в одном круге

Концепцию, популяризированную Робертом Мартином (Дядюшкой Бобом), лучше всего иллюстрирует знаменитая диаграмма концентрических кругов. В самом центре — сущности (Entities) — бизнес-правила вашего приложения. Они не знают ни о базе данных, ни о веб-фреймворке, ни о внешнем API. Это чистая логика предметной области.

Следующий круг — сценарии использования (Use Cases). Они описывают, как пользователь (или система) взаимодействует с сущностями для достижения цели. Например, "Оформить заказ" или "Получить курс валют".

Внешние круги — это механизмы доставки (контроллеры, UI) и данные (базы данных, внешние сервисы). Главное правило: зависимости направлены внутрь. Внутренние круги не зависят от внешних. Это значит, что вы можете заменить Spring на Express, а PostgreSQL на MongoDB, и ваши бизнес-правила даже не "узнают" об этом.

Ключевой принцип: Архитектура должна говорить о бизнес-задачах, а не о технологиях. Если в описании модуля фигурируют "REST", "GraphQL" или "MySQL" — он, скорее всего, находится не в том слое.

Пример 1: Простое консольное приложение на Python

Допустим, мы пишем систему для управления задачами (To-Do). Как это выглядит в чистой архитектуре?

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

  • domain/ (ядро)
    • task.py — сущность Task (id, title, is_completed).
    • task_repository.py — абстрактный интерфейс (протокол) для работы с задачами. Только методы типа save(), find_by_id(). Ни слова о файле или БД!
  • use_cases/
    • create_task.py — сценарий "Создать задачу". Принимает репозиторий (абстракцию) и данные, валидирует, создает сущность и сохраняет через репозиторий.
  • infrastructure/ (внешний слой)
    • file_task_repository.py — конкретная реализация репозитория, которая хранит задачи в JSON-файле. Она ЗАВИСИТ от абстракции из domain.
  • main.py — точка входа. Здесь мы "собираем" приложение: создаем конкретный репозиторий и передаем его в сценарий использования.

Завтра мы захотим хранить задачи в SQLite? Мы просто напишем sqlite_task_repository.py в infrastructure, не трогая domain и use_cases. Это и есть сила инверсии зависимостей.

Пример 2: Веб-API на Node.js с TypeScript

Рассмотрим бэкенд для блога. Типичная ошибка — смешивать логику в контроллерах Express. В чистой архитектуре это выглядит иначе.

  1. Сущность Post с полями и методами (например, publish()).
  2. Репозиторий PostRepository (интерфейс).
  3. Сценарий CreatePostUseCase, который принимает репозиторий, данные поста и ID автора.
  4. Контроллер (например, PostController) — часть слоя "доставки". Его задача: получить HTTP-запрос, извлечь данные, вызвать сценарий использования и вернуть HTTP-ответ. Он ничего не знает о базе данных!
  5. Реализация репозитория PrismaPostRepository с использованием Prisma ORM для работы с реальной БД.

Тесты? Вы можете протестировать CreatePostUseCase с помощью мока (заглушки) репозитория, не поднимая базу данных. Это быстро и надежно.

Важный факт: Чистая архитектура не требует горы кода для маленьких проектов. Для pet-проекта можно использовать упрощенную структуру. Но понимание принципов позволит вам масштабировать его без боли, когда он внезапно станет большим.

Пример 3: Мобильное приложение на Flutter/Dart

Здесь отлично работает подход с пакетами/папками по слоям:

  • lib/domain/ — модели (User, Product) и интерфейсы репозиториев.
  • lib/application/ (или use_cases) — бизнес-логика, провайдеры/блоки для управления состоянием.
  • lib/infrastructure/ — реализации репозиториев на Dio для сетевых запросов, на SharedPreferences для локального кэша.
  • lib/presentation/ — виджеты, страницы, контроллеры представления. Они слушают провайдеры из application и отрисовывают UI.

Это делает приложение независимым от конкретных библиотек для работы с сетью или локальным хранилищем.

С какими сложностями вы столкнетесь?

Чистая архитектура — это не серебряная пуля. Первое время будет ощущение "овер-инжиниринга" для простых задач. Возрастет количество файлов и шаблонного кода (boilerplate). Критически важно правильно определить границы сущностей и агрегатов на этапе проектирования, иначе можно создать лишнюю сложность.

Но выгода приходит с ростом проекта и сменой команд: код становится понятным (бизнес-логика изолирована), тестируемым (ядро тестируется в isolation) и поддерживаемым (технологии можно менять с минимальным риском).

FAQ: Часто задаваемые вопросы о чистой архитектуре

Чистая архитектура и MVC — это одно и то же?

Нет. MVC — это паттерн представления, который часто смешивает логику в контроллерах. Чистая архитектура — это более высокоуровневая система слоев, в которую MVC (в виде слоя Presentation/Controllers) может органично вписаться как один из внешних кругов.

Подходит ли чистая архитектура для маленьких проектов?

Для очень маленьких (например, лендинг) — это избыточно. Но для любого проекта, который потенциально может расти, или где важна тестируемость, её принципы стоит закладывать с самого начала, пусть и в упрощенном виде.

Какой язык или фреймворк лучше всего подходит?

Принципы универсальны. Они отлично ложатся на статически типизированные языки (TypeScript, Java, C#, Dart), где интерфейсы и инъекция зависимостей реализуются естественно. Но их можно применять и на Python, и на PHP, и на JavaScript.

Главная ошибка новичков?

Делать зависимости между слоями двусторонними или позволять сущностям "протекать" во внешние слои (например, возвращать объект БД напрямую в контроллер). Всегда используйте DTO (Data Transfer Objects) или простые структуры данных для обмена между слоями.