Архитектура на практике: сравнение FSD, Clean и Modular в React-приложении
Введение: проблема выбора структуры в React-приложениях
Библиотека React известна своей гибкостью и отсутствием жестких требований к структуре папок проекта. В отличие от фреймворков вроде Angular или NestJS, которые диктуют строгие правила организации кода, разработчики на React вольны группировать файлы любым удобным способом. С одной стороны, это дает свободу на ранних этапах стартапа, но с другой — ведет к хаосу по мере роста масштабов приложения.
В крупных коммерческих проектах выбор архитектурного подхода напрямую влияет на скорость разработки, простоту онбординга новых сотрудников и количество багов, возникающих при добавлении новых функций. Исторически сложилось несколько подходов: от простейшего разделения по типам файлов до сложной многослойной методологии Feature-Sliced Design (FSD) и чистой архитектуры (Clean Architecture).
До недавнего времени выбор между этими подходами строился на личных предпочтениях архитекторов и субъективных статьях. Ситуацию изменило новое научное исследование, в котором авторы провели эмпирическое сравнение шести различных архитектурных шаблонов для React, измерив стоимость внесения изменений на реальных сценариях.
Шесть подходов к организации кода
В рамках исследования были подробно изучены и протестированы следующие шесть архитектурных структур:
- Folder-by-Type (Разделение по типам). Классическая структура первых React-приложений. Весь код группируется по техническому назначению файлов: папки
components/,hooks/,utils/,pages/,services/. Компоненты лежат в одном место, бизнес-логика — в другом. - Modular Architecture (Модульный подход). Код группируется вокруг бизнес-фич или страниц. Проект делится на автономные модули (например,
modules/auth,modules/dashboard,modules/catalog). Каждый модуль содержит свои компоненты, хуки и стили. - Clean Architecture (Чистая архитектура). Подход, перенесенный из backend-разработки. Код разделен на три независимых слоя: слой домена (domain — бизнес-правила и сущности), слой данных (data — работа с API и локальным хранилищем) и слой представления (presentation — UI-компоненты React и состояние).
- FSD - Flat (Плоский Feature-Sliced Design). Упрощенный вариант методологии FSD. Проект делится на слои (
features/,entities/,shared/), но внутри них нет жестких ограничений на вложенность и типы сегментов, что снижает уровень бойлерплейта (шаблонного кода). - FSD - Standard (Стандартный Feature-Sliced Design). Полноценное внедрение методологии. Проект разделен на строго определенные слои (app, pages, features, entities, shared), каждый из которых состоит из слайсов (конкретных бизнес-фич) и сегментов (ui, model, api, lib).
- FSD - Strict (Строгий Feature-Sliced Design). Стандартный подход FSD, дополненный строгими правилами импорта. Любые импорты между слайсами должны проходить исключительно через публичные точки входа (
index.ts). Нарушения жестко контролируются правилами линтера ESLint.
Методология исследования: симуляция запросов на изменения
Чтобы объективно сравнить эти подходы, исследователи создали эталонное приложение и реализовали его структуру в шести вариантах. Затем они смоделировали набор типичных запросов на изменения (Change Requests, CR) — от простых (добавление нового поля в форму или изменение цвета кнопки) до сложных архитектурных доработок (подключение новой платежной системы, изменение логики аутентификации, кэширование сетевых запросов).
Для каждого архитектурного шаблона при выполнении CR измерялись следующие параметры:
- Change Locality (Локальность изменений). Метрика показывает, насколько «размазаны» изменения по проекту. Она вычисляется как количество папок и файлов, которые пришлось затронуть при реализации задачи. Чем меньше показатель, тем выше связность (cohesion) и тем легче поддерживать код.
- Code Size / Boilerplate (Размер кода и шаблонность). Общее количество файлов, строк кода и файлов индексации, необходимых для функционирования архитектуры.
- Import Coupling (Связанность по импортам). Сложность графа зависимостей между файлами, наличие циклических импортов.
Результаты сравнения: метрики и выводы
1. Локальность изменений и сопровождаемость
Абсолютным лидером по локальности изменений стали стандартный и строгий варианты Feature-Sliced Design. Благодаря разделению кода на автономные слайсы, подавляющее большинство задач по добавлению фич решалось в пределах одного слайса. Разработчику не нужно прыгать по всему проекту: логика, стили, типы и API конкретной фичи находятся в одной папке.
Худший результат показал подход Folder-by-Type. Простая задача по изменению логики авторизации заставила исследователей изменить файлы в папках components/auth, hooks/useAuth, services/api и pages/login. Это повышает риск забыть обновить импорт или сломать соседний компонент.
2. Объем кода и бойлерплейт
Обратной стороной высокой локальности FSD и Чистой архитектуры является колоссальный объем шаблонного кода. В FSD - Strict количество файлов увеличилось более чем на 40% по сравнению с Folder-by-Type. Это связано с необходимостью создавать файлы index.ts (Public API) для каждого слайса и сегмента, чтобы излировать внутреннюю реализацию.
Clean Architecture также продемонстрировала избыточность: разработчикам приходится писать функции-мапперы для конвертации моделей данных из слоя data в слой domain, а затем в UI-модели слоя presentation.
3. Сложность связей и риски рефакторинга
Внедрение FSD - Strict с ESLint-правилами полностью ликвидировало циклические зависимости и хаотичные импорты. Архитектура приложения стала прозрачной. Однако рефакторинг в такой системе стал гораздо дороже. Если бизнес-требования меняются и функционал нужно перенести из сущности (Entity) в фичу (Feature), разработчику приходится переписывать множество публичных точек экспорта и обновлять пути импорта по всему проекту.
Modular Architecture показала наиболее сбалансированные результаты для средних проектов. Она обеспечивает хорошую локальность изменений (код сгруппирован по модулям), но не требует создания десятков файлов-посредников и сложных правил импорта, как в FSD.
Анализ сильных и слабых сторон архитектур
- Folder-by-Type. Идеально подходит для небольших проектов, хакатонов и прототипов (MVP). Имеет минимальный порог входа и полное отсутствие бойлерплейта. На крупных проектах превращается в «спагетти-код» с запутанным графом зависимостей.
- Modular Architecture. Оптимальный выбор для большинства команд среднего размера. Логика фич сгруппирована, структуру легко понять без чтения многостраничных гайдлайнов. Слабое место — риск размывания границ модулей со временем, если нет автоматического контроля импортов.
- Clean Architecture. Хорошо работает в приложениях со сложной математической или бизнес-логикой на клиенте, которая живет независимо от UI (например, графические редакторы или сложные финансовые калькуляторы). Для обычных CRUD-приложений этот подход избыточен и приводит к написанию сотен строк транзитного кода.
- FSD (Standard / Strict). Лучшее решение для крупных долгоживущих корпоративных приложений с десятками разработчиков. Обеспечивает строгий порядок, независимость команд и защиту от архитектурной деградации. Главные минусы — высокий порог входа, сложность проектирования связей на старте и обилие файлов-пустышек для Public API.
Рекомендации по выбору структуры
На основе эмпирических данных исследователи сформулировали практические рекомендации по выбору архитектуры:
- Если вы создаете малый проект или MVP силами 1–2 разработчиков, используйте классический Folder-by-Type или простой Modular. Это сбережет время и избавит от написания лишнего кода.
- Для средних проектов (3–10 разработчиков, планируемый срок жизни от года) оптимальным выбором станет Modular Architecture. Она структурирует код, сохраняя высокую скорость поставки фич.
- Для крупных enterprise-приложений (множество продуктовых команд, сотни компонентов) настоятельно рекомендуется внедрять Feature-Sliced Design. Накладные расходы на бойлерплейт и проектирование окупятся простотой поддержки, высокой локальностью изменений и защитой от архитектурного хаоса.

![Node.JS [ru]](/api/digests/it_development/daily/20260720/assets/sources/we-use-js.jpg)