Преимущества FSD - как архитектура делает фронтенд управляемым и масштабируемым

05 января 2026
Автор

Олег Марков

Введение

Feature-Sliced Design (FSD) — это подход к архитектуре фронтенда, в котором вы организуете проект вокруг функциональных возможностей продукта, а не вокруг технических слоев. Вместо привычной структуры по типам (components, services, hooks) вы получаете слои вроде app, processes, pages, features, entities, shared, а внутри — срезы под конкретные доменные сущности и фичи.

Здесь мы разберем, какие реальные преимущества дает FSD:

  • почему код становится понятнее и предсказуемее;
  • как FSD влияет на скорость разработки и онбординг новых разработчиков;
  • какие организационные плюсы появляются при росте команды;
  • как FSD снижает уровень «архитектурного хаоса» в долгоживущих проектах;
  • чем FSD отличается от классической «атомарной» или слойной архитектуры.

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

Что такое FSD и зачем он нужен

Краткое напоминание концепции

В основе FSD лежат два ключевых принципа:

  1. Организация по функционалу (feature-first)
    Код группируется по фичам и сущностям домена. Любой разработчик может быстро найти все, что относится к конкретной возможности продукта.

  2. Явные уровни абстракции (слои)
    Слои определяют «насколько высоко» над бизнес-логикой находится модуль и кто имеет право от кого зависеть.

Базовые слои (в классическом варианте FSD):

  • app — инициализация приложения, роутинг, провайдеры;
  • processes — сквозные бизнес-процессы;
  • pages — композиция фич и сущностей под конкретную страницу;
  • features — пользовательские фичи (логин, добавление в корзину, фильтрация и т.п.);
  • entities — доменные сущности (User, Product, Order);
  • shared — переиспользуемые утилиты, UI-компоненты, либы.

Преимущества FSD для структуры и читаемости кода

Предсказуемая навигация по проекту

Главное, что вы начинаете ощущать — это предсказуемость. Когда вам говорят «исправь баг в фильтре товаров», вы почти автоматически понимаете, что искать:

  • слой: features (функциональность);
  • срез: product-filter или похожий;
  • внутри — папки ui, model, lib и т.п.

Смотрите, как это выглядит в типичном проекте:

src/
  app/
    providers/
    routing/
  pages/
    product-list/
      ui/
      model/
  features/
    product-filter/
      ui/
      model/
      lib/
    add-to-cart/
      ui/
      model/
  entities/
    product/
      ui/
      model/
    cart/
      ui/
      model/
  shared/
    ui/
    lib/
    api/

Как видите, здесь идея в том, что вы почти бездумно идете по дереву:

  • «страница списка товаров» → pages/product-list;
  • «фича фильтрации» → features/product-filter;
  • «сущность товара» → entities/product.

В результате:

  • меньше времени уходит на поиск кода;
  • меньше «магии» и неочевидных зависимостей;
  • легче понять границы ответственности каждого модуля.

Минимизация «размазанных» изменений

В архитектуре «по слоям» (например, components, hooks, services) одна фича часто оказывается разбросана по всему проекту:

  • часть логики в одном сервисе;
  • компоненты — в другой папке;
  • хелперы — в третьей.

В FSD все, что относится к фиче, лежит рядом. Когда вы дорабатываете фичу, вы в основном работаете в одной директории, а не прыгаете по всему проекту.

Посмотрите на пример:

features/
  add-to-cart/
    ui/
      AddToCartButton.tsx
    model/
      hooks/
        useAddToCart.ts
      services/
        addToCartApi.ts
      types.ts
  • Вся логика добавления в корзину — в одном месте.
  • Любой новый разработчик быстро видит, что относится к конкретной пользовательской возможности.

Преимущества FSD для масштабируемости и модульности

Локализация изменений и изоляция фич

Когда у вас есть четкие слои и срезы, вы можете намного проще:

  • отключать фичи;
  • рефакторить отдельные части кода;
  • выносить фичи в отдельные пакеты/репозитории.

Например, у вас появилась задача временно отключить экспериментальную фичу product-recommendations. Если она реализована как отдельный срез в features/product-recommendations, вам достаточно:

  • убрать её импорт в нужных страницах;
  • при необходимости — выключить её экспорт.

Это намного проще, чем выискивать куски логики по всему проекту.

Переиспользование сущностей и фич

Еще один важный плюс — FSD поощряет выделение сущностей (entities) и их осознанное переиспользование между фичами и страницами.

Представьте, что у вас есть сущность product, которая используется:

  • в списке товаров;
  • на карточке товара;
  • в корзине;
  • в блоке рекомендаций.

Вместо создания отдельных компонент ProductCard для каждой страницы вы держите ядро сущности в одном месте:

entities/
  product/
    ui/
      ProductCard.tsx
      ProductPrice.tsx
    model/
      types.ts
      selectors.ts
      productSlice.ts

Потом страницы и фичи просто композиционно используют это ядро:

// pages/product-list/ui/ProductListPage.tsx

import { ProductCard } from "@/entities/product/ui/ProductCard"
import { Product } from "@/entities/product/model/types"

type Props = {
  products: Product[]
}

export const ProductListPage = ({ products }: Props) => {
  // Здесь мы используем сущность Product в контексте страницы
  return (
    <div>
      {products.map((product) => (
        <ProductCard key={product.id} data={product} />
      ))}
    </div>
  )
}

// Здесь ProductCard переиспользуемый компонент сущности Product // Страница лишь композитно собирает UI не дублируя доменную логику

Как видите, это поощряет:

  • повторное использование сущностей;
  • единый источник правды для бизнес-логики конкретной сущности;
  • меньше дублирования кода.

Управляемый рост проекта

Когда проект растет, архитектура без четких границ быстро превращается в «слоеный пирог» с неконтролируемыми связями. FSD предлагает:

  • ограничить направления зависимостей (кто от кого может зависеть);
  • явно задавать публичные интерфейсы модулей;
  • минимизировать неявные связи.

Вы можете, например, установить правило:

  • shared не может зависеть от features и entities;
  • entities не может зависеть от features;
  • features могут зависеть от entities и shared;
  • pages могут зависеть от features, entities, shared;
  • app может зависеть от всех.

Так вы сразу предотвращаете классическую ситуацию, когда:

  • утилита из shared вдруг начинает импортировать бизнес-логику из features;
  • сущность знает о конкретной странице;
  • появляется циклическая зависимость.

Часто это еще и закрепляют статическим анализом (ESLint или custom-линтер): описывают разрешенные импорты между слоями.

Преимущества FSD для командной работы

Явное разделение зон ответственности

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

  • ответственных за сущности (доменные модели — entities);
  • ответственных за фичи (features);
  • ответственных за интеграцию и UX на уровне страниц (pages).

Команда начинает мыслить:

  • не «кто занимается кнопками»,
  • а «кто отвечает за корзину», «кто за профиль пользователя», «кто за платежи».

Это совпадает с тем, как устроен продукт, а не с тем, как устроен фреймворк.

Ускоренный онбординг новых разработчиков

Новому разработчику не нужно запоминать сложные внутренние правила типа:

  • «эти компоненты лежат тут, но используются там»;
  • «вот этот хук используется только для заказов, хотя лежит в общей папке».

Достаточно объяснить два принципа:

  1. Все, что связано с пользовательской возможностью, ищите в features.
  2. Все, что связано с сущностью домена, — в entities.

Затем показать пару примеров — и человек уже может самостоятельно ориентироваться.

Посмотрите на небольшой фрагмент структуры:

features/
  auth-by-username/
    ui/
      LoginForm.tsx
    model/
      slice.ts
      selectors.ts
      types.ts
    lib/
      validators.ts

Даже если вы впервые видите проект, легко догадаться:

  • LoginForm.tsx — форма логина;
  • slice.ts — Redux slice или аналог для состояния логина;
  • validators.ts — логика проверки полей.

Удобное разделение задач

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

  • «доработать features/product-filter»;
  • «расширить модель entities/user настройками фильтров» (если они привязаны к пользователю).

Так задачи из баг-трекера напрямую соотносятся с архитектурой проекта. Это снижает:

  • количество коммуникаций;
  • риск, что разработчик полезет не в ту часть системы;
  • время на оценку задач.

Преимущества FSD для качества кода и снижения технического долга

Управляемые публичные интерфейсы

FSD предполагает, что у каждого среза (feature, entity, page) есть публичный интерфейс — то, что разрешено импортировать снаружи. Чаще всего это файл вроде index.ts или public-api.ts в корне среза.

Давайте разберемся на примере:

features/
  add-to-cart/
    ui/
      AddToCartButton.tsx
    model/
      useAddToCart.ts
    index.ts
// features/add-to-cart/index.ts

export { AddToCartButton } from "./ui/AddToCartButton"
export { useAddToCart } from "./model/useAddToCart"

// Внешний мир видит только то что мы явно экспортировали здесь

// Здесь мы формируем публичный интерфейс фичи // Внешний код не должен импортировать файлы напрямую из ui или model

Теперь в других частях приложения вы импортируете только из корня фичи:

// pages/product-card/ui/ProductCardPage.tsx

import { AddToCartButton } from "@/features/add-to-cart"

// Здесь страница использует только публичный интерфейс фичи
// Внутреннюю структуру фичи можно менять без влияния на внешние модули

Преимущества такого подхода:

  • можно безопасно менять внутреннюю структуру фичи;
  • упрощается рефакторинг;
  • нет «случайных» зависимостей от внутренних деталей реализации.

Снижение связанности и упрощение тестирования

Когда у вас явно выделены сущности и фичи, тестировать становится проще:

  • вы тестируете фичу как отдельный модуль;
  • вы тестируете сущность как отдельный модуль;
  • страницы — это в основном композиция, их тесты содержат минимум логики.

Например, модуль features/add-to-cart может иметь свои unit-тесты:

// features/add-to-cart/model/useAddToCart.test.ts

import { renderHook, act } from "@testing-library/react"
import { useAddToCart } from "./useAddToCart"

test("добавляет товар в корзину", () => {
  const { result } = renderHook(() => useAddToCart())

  act(() => {
    result.current.add({ id: "1", name: "Product" })
  })

  expect(result.current.items).toHaveLength(1)
  expect(result.current.items[0].id).toBe("1")
})

// Здесь мы тестируем поведение фичи в отрыве от страницы

// Тесты изолированы от конкретной страницы и от общего состояния приложения // Это повышает надежность и облегчает рефакторинг

В результате:

  • проще поддерживать тестовое покрытие;
  • изменения не ломают пол-репозитория тестов;
  • можно развивать фичи независимо друг от друга.

Контроль за ростом shared-слоя

Одна из типичных проблем крупных фронтенд-проектов — гигантская папка shared, в которую складывают «все, что неясно куда деть». В FSD есть встроенный «предохранитель»:

  • сначала вы пытаетесь реализовать логику внутри конкретной фичи или сущности;
  • только потом, если реально появляется повторное использование, выносите это в shared.

Так shared растет осознанно, а не стихийно. Это существенно снижает риск:

  • захламленного набора утилит;
  • дублирующих компонентов и функций;
  • сильной связанности «всего со всем».

Преимущества FSD для работы с сложными доменами

Явное выделение доменных сущностей

В проектах с нетривиальной предметной областью (маркетплейсы, CRM, банковские системы) доменных сущностей много, и они сложные. FSD предлагает:

  • выделить сущности в отдельный слой entities;
  • строго разделять их от конкретных пользовательских фич.

Например:

entities/
  user/
  product/
  order/
  payment/
  notification/

Каждая из этих сущностей:

  • имеет свой набор типов, моделей, селекторов;
  • может иметь UI-компоненты для отображения;
  • может использоваться в нескольких фичах и процессах.

Фичи, в свою очередь, описывают то, что делает пользователь:

features/
  place-order/
  cancel-order/
  pay-order/
  apply-discount/
  subscribe-notifications/

Так, когда вы расширяете домен (например, добавляете новый статус заказа), вы:

  • вносите изменения в entities/order;
  • а потом уже дорабатываете фичи, которые зависят от этого статуса.

Проще обсуждать архитектуру с бизнесом

Еще один неожиданный плюс — разговоры с продакт-менеджерами и аналитиками становятся проще. Вы можете обсуждать:

  • сущности (User, Product, Order);
  • фичи (Place Order, Cancel Order, Pay Order).

И это напрямую соответствует архитектуре проекта:

  • «Эта фича реализована в features/place-order»;
  • «Эта сущность у нас описана в entities/order».

Таким образом:

  • меньше «перевода» между техническим и бизнес-языком;
  • проще синхронизировать требования и реализацию;
  • легче держать документацию и код в одном понятийном поле.

Преимущества FSD для внедрения новых технологий и рефакторинга

Локализованный технологический стек внутри фичи

Если вы хотите попробовать новую библиотеку (например, для работы с формами), FSD позволяет ограничить эксперимент рамками одной фичи.

Например:

  • текущий стек форм — react-hook-form;
  • вы хотите попробовать Formik только в одной фиче features/feedback-form.

Вы можете:

  • внутри этой фичи использовать другой подход;
  • при успешном результате перенести лучшие практики в другие фичи.

Это снижает риски и упрощает эксперименты.

Постепенный рефакторинг без «большого взрыва»

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

В FSD вы можете:

  • менять внутреннюю реализацию состояния в конкретной фиче;
  • сохранять ее публичный интерфейс;
  • затем постепенно переводить страницы на новый подход.

Например, фича add-to-cart использовала Redux:

// features/add-to-cart/model/index.ts

export { addToCartReducer } from "./slice"
export { useAddToCart } from "./hooks"

Со временем вы переписываете ее на локальный Zustand или jotai, но снаружи интерфейс остается тот же:

// features/add-to-cart/index.ts

export { useAddToCart } from "./model/useAddToCart"

Страницам и другим фичам все равно, что внутри:

  • вы постепенно обновляете модули;
  • не ломаете внешние зависимости.

Преимущества FSD для DX (Developer Experience)

Снижение когнитивной нагрузки

Когда проект организован по FSD, у вас меньше вещей, которые надо держать в голове:

  • вы знаете, где искать фичу;
  • вы понимаете, как устроены зависимости;
  • вы не тратите время на то, чтобы «разгадать» структуру.

Это особенно заметно:

  • в долгосрочной перспективе;
  • когда вы переключаетесь между задачами и контекстами;
  • когда работаете в проекте время от времени.

Единообразие подходов

FSD поощряет использование одних и тех же паттернов:

  • структура среза (ui, model, lib);
  • экспорт через публичный интерфейс;
  • единый подход к именованию.

Это снижает количество «индивидуальных стилей» внутри проекта. В результате:

  • меньше сюрпризов в чужом коде;
  • проще ревьюить изменения;
  • легче поддерживать договоренности по архитектуре.

Облегчение автоматизации и инструментов

Из-за четкой структуры становится проще:

  • автоматизировать генерацию модулей (CLI для создания новой фичи);
  • анализировать зависимости и находить нарушения архитектурных правил;
  • внедрять code mods для массовых изменений.

Например, вы можете написать простой скрипт, который создает каркас новой фичи:

# Пример условной команды генерации фичи
yarn gen feature add-to-favorites

Форма генератора создаст:

features/
  add-to-favorites/
    ui/
      AddToFavoritesButton.tsx
    model/
      useAddToFavorites.ts
    index.ts

Вы сразу получаете:

  • структуру в соответствии с архитектурой;
  • готовый публичный интерфейс;
  • минимальный шаблон кода.

Это ускоряет задачу «завести новую фичу» и снижает риск, что кто-то нарушит договоренности по структуре.

Заключение

Feature-Sliced Design дает системе координат, в которой фронтенд-проект растет предсказуемо и управляемо. Основные преимущества FSD можно свести к нескольким ключевым эффектам:

  • Повышенная читаемость и предсказуемость — вы всегда знаете, где искать код, связанный с конкретной фичей или сущностью.
  • Лучшее масштабирование — как по размеру кодовой базы, так и по количеству разработчиков и команд.
  • Снижение уровня хаоса и технического долга — из-за четких границ, публичных интерфейсов и контролируемых зависимостей.
  • Удобная работа с доменными сущностями — архитектура напрямую отражает предметную область и пользовательские сценарии.
  • Упрощенный рефакторинг и эксперименты — изменения можно делать локально, не устраивая «бурю» во всем проекте.
  • Улучшенный developer experience — меньше когнитивной нагрузки, единообразные подходы, облегченное ревью и онбординг.

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

Частозадаваемые технические вопросы по теме и ответы

Как постепенно внедрять FSD в уже существующий проект

  1. Не пытайтесь переписать все сразу.
  2. Начните с новых фич — кладите их в features, сущности — в entities, общие вещи — в shared.
  3. При доработке старого модуля постепенно выносите его части в FSD-структуру.
  4. Добавьте правило: новые модули — только в FSD-слоях.
  5. Постепенно вы «обрастете» островками FSD, которые со временем займут большую часть проекта.

Как контролировать допустимые импорты между слоями

  1. Введите архитектурные правила зависимостей (например, features не импортирует pages).
  2. Настройте ESLint с плагинами вроде eslint-plugin-boundaries или eslint-plugin-import.
  3. Опишите в конфиге разрешенные пути импорта между слоями.
  4. Включите правило в CI, чтобы запретить merge при нарушении.

Как организовывать типы и API-клиенты в FSD

  1. Базовые API-клиенты (HTTP-клиент, конфиг, базовые обертки) — в shared/api.
  2. Типы и адаптеры, специфичные для сущности — в entities/<entity>/model.
  3. Фича может использовать API сущности через её модель или собственные клиентские функции в features/<feature>/model.
  4. Избегайте прямых вызовов глобального API-клиента из любого места — лучше через слой сущностей.

Как лучше организовать роутинг в FSD проекте

  1. Основная конфигурация маршрутов — в app/routing.
  2. Конкретные компоненты страниц — в pages/<page-name>/ui.
  3. В конфиге маршрутов импортируйте только публичный компонент страницы, не завязываясь на внутреннюю структуру фич.
  4. Если у страницы сложная логика загрузки данных, выносите её в pages/<page-name>/model и подключайте в корневом компоненте страницы.

Можно ли совмещать FSD с монорепой и разделением на пакеты

  1. Да, FSD-слои можно выносить в отдельные пакеты монорепы (@app/*, @entities/*, @features/*, @shared/*).
  2. Следите, чтобы между пакетами сохранялись те же правила зависимостей, что и внутри одного репо.
  3. Общие сущности и фичи можно выделять в отдельные пакеты и переиспользовать в нескольких приложениях.
Стрелочка влевоНедостатки FSD - drawbacksСравнение с Atomic Design - vs-atomicСтрелочка вправо

Все гайды по Fsd

Открыть базу знаний

Отправить комментарий