Как мы использовали Feature-Sliced Design
для архитектуры фронтенд-приложения

30 декабря 2025 · ? просмотров · ? мин
монитор с проектом на темном фоне с текстом FSD
...
Содержание
В современных фронтенд-проектах архитектура довольно быстро перестаёт быть «внутренним делом команды» и начинает напрямую влиять на скорость разработки, качество изменений и стоимость поддержки. По мере роста приложения усложняется навигация по кодовой базе, увеличивается количество пересечений между модулями, и любые изменения начинают требовать больше времени и осторожности. Нам было важно выбрать подход, который задаёт понятные правила, но при этом остаётся практичным и не мешает развиваться продукту в реальных условиях.

В данной статье мы расскажем про то, как мы использовали Feature-Sliced Design для архитектуры фронтенд-приложения и адаптировали методологию под page-based подход: какие слои оставили, какие добавили, как организовали страницы как самостоятельные модули и за счёт чего снизили destructive decoupling, сохранив удобство масштабирования и поддержки.

Что такое Feature-Sliced Design

Feature-Sliced Design (FSD) — архитектурный подход к разработке фронтенд-приложений. Если упростить, это набор правил, который помогает сделать проект более понятным, читаемым, поддерживаемым и масштабируемым. При этом важно учитывать, что в реальных проектах FSD почти всегда интерпретируют и адаптируют под контекст: у команд разные домены, разная динамика изменений и разная потребность в переиспользовании.

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

Какие слои мы оставили и почему

Из слоёв, которые предлагает методология, мы оставили следующий набор
и дополнили его своим слоем для стилей:
  • app — глобальные элементы приложения: роутинг, провайдеры, сторы, инициализация и прочая инфраструктура, важная для всего приложения.
  • pages — модули страниц и локальная логика, относящаяся к конкретным страницам.
  • features — пользовательские сценарии уровня продукта (например, авторизация).
  • shared — переиспользуемые модули, которые можно относительно безболезненно применять в разных местах приложения и потенциально переносить в другие проекты.
  • styles — отдельный слой, который мы добавили: глобальные стили приложения, SCSS-константы и миксины.
Таким образом структура осталась FSD-подобной, но стала проще и ближе к тому, как фактически живёт продуктовая разработка.

Page-based подход внутри FSD

Наш вариант можно описать как page-based: мы делаем страницы основными архитектурными единицами. Каждая страница
— самостоятельный модуль, который содержит всё необходимое
для работы: UI, бизнес-логику, локальные типы, стили и вспомогательные утилиты. Благодаря этому навигация по проекту становится естественной: чтобы понять, как работает конкретная часть продукта, чаще всего достаточно перейти в папку соответствующей страницы и видеть всё в одном месте.

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

Как устроен модуль страницы в pages

Страница на слое pages обычно включает всё, что нужно только этой странице и не предполагается к использованию где-то ещё. Внутри страницы мы допускаем локальную модульность: крупные части можно разбивать
на подмодули, заводить helpers, хуки, константы и т.п.
Это не про усложнение, а про то, чтобы сохранять структуру читаемой по мере роста функциональности.

Отдельно мы договорились, что у модуля должна быть публичная точка входа — как правило, index.tsx (или index.ts), который выступает публичным API. 
Это позволяет фиксировать границы: наружу экспортируется только то,
что действительно нужно потребителям, а внутренние детали (локальные типы, стили, хелперы, подкомпоненты) остаются внутри модуля.

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

Правило миграции: когда выносить код выше

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

Практически мы используем такие критерии:

  • переносим в shared, если это универсальный компонент/утилита без жёсткой привязки к домену страницы и его можно использовать в других контекстах;
  • переносим в features, если это полноценный пользовательский сценарий уровня продукта (например, авторизация), который логически живёт «над страницами»;
  • оставляем/переносим в app, если речь про инфраструктуру и сквозную конфигурацию приложения.
Так у нас формируется контролируемое переиспользование: shared растёт только за счёт действительно общих вещей, а features остаётся слоем про сценарии, а не «свалкой всего полезного».
Как это решает проблему destructive decoupling
Одна из проблем, которую мы хотели устранить, — destructive decoupling: ситуацию, когда один логический модуль оказывается «размазанным» по разным папкам проекта (часть — в компонентах, часть — в утилитах, часть — в типах и т.д.). Page-based подход снижает вероятность такого расползания: всё, что относится к странице, находится вместе, и команде проще держать контекст. Когда переиспользование становится реальным, перенос выполняется осознанно и в правильный слой.

В результате у нас получилась структура, основанная на страницах приложения: каждая страница содержит модули, необходимые для своей работы. По сути это даёт эффект, близкий к rich domain pattern на фронтенде — доменная логика и UI остаются собраны вокруг своего контекста, а общие части постепенно оформляются как библиотека.
Пример: декомпозиция типового компонента на странице Bids
Наглядный пример — типовой компонент “элемент списка”, который относится к странице Bids. В таком случае мы размещаем его в ui сегменте страницы. Внутри компонента допускаем локальные вложенные элементы
и вспомогательные части, которые имеют смысл только в рамках этой страницы (например, локальные подкомпоненты и хелперы
— в соответствующих локальных папках).

При этом элементы, которые можно переиспользовать в других местах приложения или даже в других проектах, мы выносим в shared. 
Например, общий Button или универсальный форматтер даты — это хорошие кандидаты на переиспользование и должны жить отдельно от конкретной страницы. Так сохраняется логика: доменно-специфичное остаётся рядом
со страницей, а универсальное постепенно становится общим фундаментом проекта.
Итоги
Мы использовали FSD как основу, но адаптировали методологию под практические потребности проекта, сделав акцент на page-based структуре. Это помогло собрать связанный код в одном месте, упростить навигацию, снизить destructive decoupling и закрепить ясные правила, когда и куда переносить код при появлении переиспользования. 

В результате архитектура остаётся понятной по мере роста продукта,
а команда получает предсказуемую структуру и понятные границы модулей.
Оценить материал
Остальные статьи по react