Feature-Sliced Design

Feature-Sliced Design (FSD) — методология организации файловой структуры фронтенд-приложения, делящая код на слои (layers) и нарезки по фичам (slices) с явными правилами зависимостей между ними.

Зачем нужно

Без чёткой структуры большое приложение превращается в «спагетти»: components/ с сотнями файлов без классификации, circular imports, непонятно где добавлять новый код. FSD решает это через стандартизацию: каждый разработчик знает куда положить файл и что может импортировать. Правило «слой может импортировать только из слоёв ниже» предотвращает circular deps и сложные зависимости.

Где используется

  • Крупные SPA с несколькими фичами и командами
  • React/Vue/Angular проекты с желанием стандартизировать структуру
  • Распространено в русскоязычном сообществе; набирает популярность глобально

Структура FSD

src/
├── app/           ← 7. Настройки приложения, провайдеры, роутер
├── pages/         ← 6. Страницы (композиция виджетов)
├── widgets/       ← 5. Самостоятельные секции страниц (Header, Sidebar)
├── features/      ← 4. Пользовательские сценарии (auth, cart-add)
├── entities/      ← 3. Бизнес-сущности (User, Product, Order)
├── shared/        ← 2. Переиспользуемый код без бизнес-логики
└── (processes/)   ← 1. Устаревший слой, редко используется

Правило: каждый слой может импортировать только из слоёв ниже себя. featuresentitiesshared: допустимо. sharedfeatures: запрещено.

Структура слайса

features/
  auth/             ← слайс "авторизация"
    ui/             ← компоненты LoginForm, AuthButton
    model/          ← store, actions, selectors (state)
    api/            ← запросы к API
    lib/            ← утилиты для фичи
    index.ts        ← публичный API слайса (единственная точка импорта)

Пример: product-card фича

entities/
  product/
    ui/
      ProductCard.tsx     ← карточка товара
      ProductPrice.tsx    ← цена с форматированием
    model/
      productModel.ts     ← тип Product, селекторы
    api/
      productApi.ts       ← fetch /api/products/:id
    index.ts              ← export { ProductCard, productModel, productApi }

features/
  add-to-cart/
    ui/
      AddToCartButton.tsx ← кнопка добавления
    model/
      cartStore.ts        ← zustand store корзины
    index.ts

pages/
  product-page/
    ui/
      ProductPage.tsx     ← компонует entities + features

// ProductPage.tsx — только импорты из слоёв ниже
import { ProductCard } from 'entities/product';
import { AddToCartButton } from 'features/add-to-cart';
// НЕ: import что-то из 'pages/another-page'

Публичный API слайса (index.ts)

// entities/product/index.ts
// Только то, что разрешено использовать снаружи
export { ProductCard } from './ui/ProductCard';
export { ProductPrice } from './ui/ProductPrice';
export type { Product } from './model/productModel';
// Внутренние детали НЕ экспортируются

// Использование:
import { ProductCard, type Product } from 'entities/product';
// НЕ: import { ProductCard } from 'entities/product/ui/ProductCard'

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

  • Нарушение правила зависимостейshared импортирует из features; используйте линтер @feature-sliced/eslint-plugin для автоматической проверки.
  • Слишком мелкие слайсы — создают слайс для каждого компонента; слайс должен соответствовать бизнес-сущности или фиче, а не UI-элементу.
  • Игнорируют index.ts — импортируют напрямую из внутренних файлов (entities/product/ui/ProductCard), нарушая инкапсуляцию.

Связанные темы

Ресурсы