Gernar
Frontend DeveloperАрхитектура frontend

Интервью-вопрос

Что такое чистая архитектура

Чистая архитектура помогает отделить правила приложения от React, API-клиента, роутера и других деталей. На интервью важно объяснить направление зависимостей, пользу для тестов и цену лишних абстракций.

Добавлен
Редакция

Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.

🐰0
🥚0

Мини-квиз

Проверка перед разбором

Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.

Вопрос 1 из 60 правильно

Как лучше коротко объяснить чистую архитектуру?

Вы отвечаете на базовый вопрос и хотите сразу показать практический смысл.

Варианты ответа

Разбор

Разобраться, а не зазубрить

Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.

Базовая идея

Чистая архитектура отвечает на вопрос: что в приложении должно быть стабильным, а что можно заменить с минимальным ущербом. Внутри находится ядро: правила предметной области и сценарии приложения. Снаружи находятся детали: UI-фреймворк, API-клиент, браузерные API, роутер, analytics, storage.

Главная мысль для ответа простая. Внутренний код не должен знать о внешних деталях. Если use case импортирует axios, вызывает localStorage или возвращает React-элемент, он уже привязан к инфраструктуре. Тогда смена API-клиента, SSR-ограничения или новый экран могут потянуть переписывание бизнес-логики.

На интервью не обязательно рисовать классические круги Роберта Мартина. Достаточно объяснить правило зависимостей, слои и практическую пользу: меньше связанности, проще тесты, безопаснее изменения.

Как это звучит во frontend

Хорошая формулировка может быть такой:

Я понимаю чистую архитектуру как разделение приложения на стабильное ядро и внешние детали. React-компоненты, роутер, API-клиент и storage находятся ближе к внешнему слою. Сценарии приложения и доменные правила не должны зависеть от них напрямую. Поэтому компонент может вызвать use case, а use case работает через интерфейс repository или gateway.

Такая фраза показывает не только знание термина, но и связь с ежедневной frontend-разработкой. Вы не обещаете магически заменить React на Vue за день. Вы говорите точнее. Если логика отделена от UI и инфраструктуры, область изменений становится меньше.

Куда класть код

Самая частая практическая проверка выглядит так. Вам дают кусок логики и спрашивают, где ему место. Не отвечайте только названиями папок. Лучше покажите критерий выбора.

Как выбрать место для логики

1Правило относится к предметной области и не зависит от экрана?
Оставьте его в домене или use case, не привязывайте к React.
2Код вызывает HTTP, localStorage, аналитику или роутер?
Спрячьте его за адаптером или gateway, чтобы ядро знало только контракт.
3Логика нужна только для отображения одного компонента?
Можно оставить ее рядом с компонентом, если она не стала бизнес-правилом.
4Требование часто меняется или уже ломало соседние экраны?
Проведите явную границу и добавьте тест на сценарий, а не только на UI.

Что защищать на границах слоев

Доменное правилоentity

Храните инварианты отдельно от React. Иначе смена формы или UI может сломать правило расчета, валидации или доступности действия.

Сценарий пользователяuse case

Описывает шаги вроде создания заказа или сохранения профиля. Если спрятать его в компоненте, сценарий будет трудно тестировать и переиспользовать.

Преобразование данныхadapter

Маппит DTO в модель экрана или домена. Без адаптера изменения API расползаются по компонентам и дают тихие баги в UI.

Фреймворк и средаReact, router, storage

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

Пример с зависимостью от API

Плохой вариант: use case напрямую знает про fetch, форму ответа сервера и не задает границу для ошибок или отмены. Такой код сложнее тестировать. Еще хуже, если его потом вызывают из useEffect без AbortController. При быстрой смене userId старый ответ может перезаписать новый профиль в UI, а ошибка сети превратится в непонятное пустое состояние.

// Плохо: сценарий зависит от HTTP, DTO и не проверяет ответ
export async function loadProfile(userId: string) {
  const response = await fetch(`/api/users/${userId}`);
  const dto = await response.json();

  return {
    name: dto.full_name,
    isActive: dto.is_active,
  };
}

Более безопасный вариант: use case зависит от контракта. Конкретный HTTP-клиент, проверка response.ok, mapper DTO, нормализация ошибок и отмена запроса живут на внешней границе.

type Profile = {
  name: string;
  isActive: boolean;
};

type ProfileRepository = {
  getById(userId: string): Promise<Profile>;
};

export async function loadProfile(
  userId: string,
  repository: ProfileRepository,
) {
  const profile = await repository.getById(userId);

  if (!profile.isActive) {
    return { ...profile, warning: "Профиль неактивен" };
  }

  return profile;
}

Здесь сценарий можно протестировать без браузера, сети и React. Если завтра вместо fetch будет другой клиент или изменится DTO, вы меняете адаптер, а не правило сценария. В React-слое остается понятная задача: создать AbortController при запросе, передать signal в HTTP-адаптер, отменить запрос в cleanup, показать loading, error или empty state и не применять результат отмененного запроса.

Как не уйти в overengineering

Чистая архитектура не требует превращать каждую кнопку в отдельный слой. Если код просто раскрывает dropdown, переключает tab или форматирует локальную подпись, он может остаться рядом с компонентом. Проблема появляется, когда в компоненте смешиваются правила доступа, маппинг API, обработка ошибок, аналитика и сложный сценарий сохранения.

На интервью полезно проговорить границу. Выносите то, что является правилом приложения, должно тестироваться отдельно или может использоваться в разных UI. Остальное можно держать проще, чтобы команда не платила цену абстракций без пользы.

Безопасный поток
  1. 1Компонент получает действие пользователя
  2. 2Вызывает use case с нужными данными
  3. 3Use case работает через интерфейс repository
  4. 4Адаптер вызывает API, проверяет ответ и маппит DTO
  5. 5Компонент отображает loading, результат, empty state и ошибки
Опасный поток
  1. 1Компонент сам собирает бизнес-правила
  2. 2Use case импортирует axios, router или localStorage
  3. 3DTO из API проходит прямо в JSX
  4. 4Запрос нельзя отменить при смене экрана или userId
  5. 5Старый ответ перетирает актуальное состояние UI
  6. 6Изменение API ломает несколько экранов сразу

Что сказать про тестируемость

Тестируемость здесь не самоцель, а следствие слабой связанности. Если use case получает зависимости через аргументы или через контейнер на внешнем уровне, его можно проверить обычным unit-тестом. Вам не нужно поднимать DOM, мокать router и настраивать сетевой слой ради проверки бизнес-правила.

Практический риск обратного подхода такой. Тесты становятся хрупкими. Они проверяют не правило, а детали реализации экрана. После рефакторинга компонента тест падает, хотя поведение пользователя не изменилось.

Отношение к MVC, MVVM и React-паттернам

MVC, MVVM, hooks, контейнерные компоненты и feature-based структура не конфликтуют с чистой архитектурой сами по себе. Они чаще описывают организацию внешнего слоя или слоя представления. Чистая архитектура находится уровнем выше и задает вопрос: кто от кого зависит.

Поэтому сильный ответ не спорит, какой паттерн правильный. Он показывает, что React-хук может быть адаптером к use case, view model может готовить данные для экрана, а доменное правило все равно не должно импортировать JSX или браузерный API.

Практический вывод

Если вас спрашивают про чистую архитектуру, отвечайте по цепочке: определение, правило зависимостей, frontend-пример, польза и ограничение. Так ответ звучит спокойно и прикладно.

Короткая структура ответа:

  1. Приложение делится на слои.
  2. Зависимости направлены к ядру, а не к UI и инфраструктуре.
  3. Во frontend это отделяет React-компоненты и API-адаптеры от сценариев и правил.
  4. Польза: проще тестировать, менять API, переиспользовать логику и контролировать регрессии.
  5. Цена: больше кода и договоренностей, поэтому подход нужен там, где сложность уже есть или скоро появится.

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

Где обычно ошибаются

Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.

  1. 1

    Сводить подход к структуре папок

    Папки сами по себе не дают чистую архитектуру. Если модуль useCases импортирует React-хук или конкретный API-клиент, граница уже сломана. Лучше объяснять через направление зависимостей и контракты между слоями.
  2. 2

    Писать абстракции для каждого действия

    Для простого локального состояния отдельный use case, repository и mapper могут быть лишними. Это увеличивает время изменений и делает проект тяжелее для команды. Безопаснее сказать, что уровень абстракции зависит от сложности сценария и цены ошибки.
  3. 3

    Оставлять DTO прямо в компонентах

    Если JSX читает поля серверного ответа напрямую, изменение API быстро ломает UI. Например, переименование is_active в active придется чинить во многих местах. Лучше маппить ответ в модель, которую понимает приложение.
  4. 4

    Не проектировать границу запроса

    Если компонент сам решает, как читать DTO, показывать loading, обрабатывать error и отменять запрос, один и тот же сценарий быстро расходится по экранам. При быстрой смене параметров старый ответ может перезаписать новое состояние. Лучше держать HTTP, маппинг, нормализацию ошибок и отмену на внешней границе, а в UI оставлять понятные состояния.
  5. 5

    Прятать побочные эффекты в домене

    Доменное правило не должно само ходить в сеть, писать в localStorage или отправлять аналитику. Иначе его сложнее тестировать, а порядок эффектов начинает зависеть от деталей браузера. Побочные эффекты лучше держать во внешнем слое или в адаптере.
  6. 6

    Обещать легкую замену React на другой фреймворк

    Чистая архитектура снижает зависимость бизнес-логики от React, но не делает миграцию UI бесплатной. Компоненты, состояние экрана, роутинг и дизайн-система все равно потребуют работы. Сильнее звучит формулировка, что подход уменьшает область изменений, а не отменяет их.

Follow-up

Что могут спросить дальше

Короткие ответы на вопросы, которыми проверяют, умеете ли вы применять чистую архитектуру во frontend-коде.

Живые ответы

Видео с похожим вопросом

Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.

Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.

Содержание