Правила Frontend FSD И Atomic Design
Назначение
Документ уточняет применение Feature-Sliced Design (FSD) и Atomic Design в frontend-приложениях Pin на примере pin-admin. Цель — однозначно определить, где какой код лежит, как разделять переиспользуемые компоненты, как описывать слои в документации.
Базовые слои FSD
src/
app/ // entrypoint, providers, theme, router, global config
pages/ // route components, TanStack Router file-based
widgets/ // composite UI fragments (header, sidebar, command palette)
features/ // user-facing actions (login, schema-publish, record-create)
entities/ // client models + API adapters + stores + query keys + selectors
shared/ // ui (atoms/molecules/organisms), lib, hooks, stores, theme, config
Зависимости между слоями
app ← pages ← widgets ← features ← entities ← shared
appимпортирует из всех нижележащих.pagesимпортирует изwidgets,features,entities,shared.widgetsимпортируют изfeatures,entities,shared.featuresимпортируют изentities,shared.entitiesимпортируют только изshared.sharedни от кого не зависит.
Обратные импорты запрещены и должны быть пойманы ESLint-конфигом (eslint-plugin-boundaries).
Слой app
| Содержимое | Примеры |
|---|---|
| Корневой компонент | App.tsx, main.tsx |
| Providers | <HeroUIProvider>, <QueryClientProvider>, <ThemeProvider>, <RouterProvider>, <ErrorBoundary> |
| Глобальные guards | AuthGuard, RoleGuard, TenantContextProvider, FacilityContextProvider |
| Глобальная конфигурация | Environment variables, feature flags |
| Глобальные стили | index.css, theme provider css |
Слой pages
| Правило | Описание |
|---|---|
| Файловая структура | TanStack Router file-based (например, pages/pin/$catalogId/records.tsx). |
| Толщина страницы | Тонкая — только композиция widgets + features + entities + загрузка данных через TanStack Query. |
| Запрет бизнес-логики | Любая логика >5 строк выносится в features/ или widgets/. |
| Route guards | Применяются через TanStack Router beforeLoad. |
Слой widgets
| Содержимое | Примеры |
|---|---|
| HeaderAppBar | Top bar с логотипом, breadcrumbs, user menu, theme toggle |
| SidebarNav | Левое меню с навигацией по модулям |
| Breadcrumbs | Хлебные крошки на основе TanStack Router |
| CommandPalette | Ctrl+K палитра |
| NotificationsBell | Bell-иконка с unread count |
| ThemeToggle | Переключатель темы |
| LanguageSwitcher | Переключатель локали |
| FacilitySwitcher | Выбор facility |
Правила widgets
- Виджет — это композиция: использует molecules + features + entities.
- Не зависит от конкретной страницы; одна и та же реализация на N страниц.
- Имеет свой документ в
04-frontends/pin-admin/04-widgets/<widget>.md.
Слой features
| Содержимое | Примеры |
|---|---|
| CatalogRecordCreate | Форма создания записи с валидацией Zod и mutation |
| SchemaPublish | Кнопка + диалог публикации схемы |
| FilterBuilder | Конструктор фильтров для таблицы записей |
| QrScan | Открытие сканера + резолв QR + действия |
| FileUploadSignedUrl | Полный flow загрузки файла через signed URL |
| AutofillTestRun | Запуск тестового прогона autofill стратегий |
Правила features
- Feature — это пользовательское действие или бизнес-цель.
- Содержит: UI-компонент(ы), хук с мутацией/запросом, locale-строки, документацию.
- Запрещено импортировать features из других features (низшая coupling).
Слой entities
| Подкаталог | Содержимое |
|---|---|
entities/<entity>/model.ts | TypeScript-тип на основе Zod-схемы. |
entities/<entity>/api-adapter.ts | Функции-обёртки над Axios для CRUD endpoints. |
entities/<entity>/store.ts | Опционально — Zustand store для UI-state, связанного с этой сущностью. |
entities/<entity>/query-keys.ts | Factory query keys для TanStack Query. |
entities/<entity>/selectors.ts | Pure functions для деривации view models. |
entities/<entity>/index.ts | Public API entity (явный re-export). |
Правила entities
- Одна папка — одна сущность из бэкенда (
catalog-definition,catalog-schema,catalog-entry,dictionary, и т.д.). - Не содержит UI-компонентов уровня страницы — только мелкие presentational компоненты, специфичные для отображения этой сущности (например,
CatalogDefinitionBadge,ChecklistRunStatusPill). - Имеет свой документ в
04-frontends/pin-admin/06-entities/<entity>/(5 файлов: model, api-adapter, store, query-keys, selectors).
Слой shared
shared/
ui/ // переиспользуемые компоненты (плоско); atoms/molecules/organisms — концептуальная классификация, без обязательных подпапок
api/ // сгенерированный API-клиент (ValIO) + хелперы (api-result, paging, invalidation)
lib/ // утилиты, форматтеры, хуки общего назначения
Atomic Design внутри shared/ui/ (баланс)
Atomic Design используется как принцип классификации сложности, а не как обязательная файловая иерархия. shared/ui/ остаётся плоским; компоненты группируются по смыслу (см. актуальный реестр в 07-shared/README.md).
Главный анти-паттерн — «оборачивание ui kit в наш ui kit» (запрещено). Примитивы HeroUI (
Button,Input,Select,Chip,Table,Checkbox,Switch,Tooltip,Avatar,Badge, …) используются напрямую из@heroui/react. НЕ создавайте тонкие обёртки-«атомы», которые лишь прокидывают пропсы в HeroUI без добавленной ценности.
Собственный компонент в shared/ui/ оправдан только когда он добавляет ценность: фиксированную композицию, доменно-нейтральную логику, обработку состояний загрузки/ошибки/пустоты, единый layout.
- Atoms — отдельного слоя нет: примитивы HeroUI используются напрямую.
- Molecules (композиция + логика) — например:
FormField,FormModal,ConfirmDialog,PageHeader,TableRowActions,ListPagination,DataTableCard,ActiveStatusChip, async-селекты (AsyncSingleSelect,AsyncMultiSelect), per-entity lookup-обёртки, document-editors. - Organisms (загрузка/состояния данных) — например:
DynamicContent/PagedDynamicContent,StateSurface, редактируемая таблица записей каталога,DocumentArrayEditor.
Перед добавлением/изменением UI сверяйтесь с полной документацией HeroUI: .claude/docs/heroui/llms-full.txt (формат <page url="…">).
Atomic Design vs FSD entities
| Слой | Что описывает |
|---|---|
shared/ui/atoms | Универсальные UI-примитивы без бизнес-смысла |
shared/ui/molecules | Композиции примитивов без бизнес-смысла |
shared/ui/organisms | Сложные UI-блоки без бизнес-привязки |
entities/<entity> | Бизнес-сущность из домена (CatalogDefinition, Dictionary, ...) |
Если компонент специфичен для конкретной сущности (например, CatalogDefinitionStatusBadge) — он в entities/catalog-definition/ui/. Если универсальный — в shared/ui/.
Именование
| Элемент | Правило | Пример |
|---|---|---|
| Component | PascalCase папка + файл index.ts или <Component>.tsx | Button/index.tsx или Button.tsx |
| Hook | camelCase с префиксом use | useCurrentFacility |
| Store | use<Name>Store | useAuthStore |
| Selector | <entity>Selectors.<derivedView> | catalogDefinitionSelectors.viewModel |
| Query key factory | <entity>Keys | catalogDefinitionsKeys.list(facilityId, filters) |
| Schema | <purpose>Schema | createCatalogDefinitionSchema |
| DTO type | <Name>Dto | CatalogDefinitionDto |
| View model | <Name>View | CatalogDefinitionView |
| Mutation | use<Action><Entity>Mutation | useCreateCatalogDefinitionMutation |
Запрещено
- Импорт из
pages/*вwidgets/*,features/*,entities/*,shared/*(обратная зависимость). - Импорт из
widgets/*вfeatures/*,entities/*,shared/*. - Импорт из
features/*в другиеfeatures/*. - Импорт
shared/ui/organismsнапрямую вpagesбез проксирования через widget/feature, если требуется бизнес-логика. - Создание
helpers.ts/utils.tsвshared/без явного назначения. Имя файла = его роль. - Использование
default exportв shared/ui (только named exports). - Тонкие обёртки над примитивами HeroUI («ui kit в нашем ui kit») без добавленной ценности — используйте HeroUI напрямую.
ESLint конфигурация
{
"plugins": ["boundaries"],
"settings": {
"boundaries/elements": [
{ "type": "app", "pattern": "src/app/*" },
{ "type": "pages", "pattern": "src/pages/*" },
{ "type": "widgets", "pattern": "src/widgets/*" },
{ "type": "features", "pattern": "src/features/*" },
{ "type": "entities", "pattern": "src/entities/*" },
{ "type": "shared", "pattern": "src/shared/*" }
]
},
"rules": {
"boundaries/element-types": ["error", {
"default": "disallow",
"rules": [
{ "from": "app", "allow": ["pages", "widgets", "features", "entities", "shared"] },
{ "from": "pages", "allow": ["widgets", "features", "entities", "shared"] },
{ "from": "widgets", "allow": ["features", "entities", "shared"] },
{ "from": "features", "allow": ["entities", "shared"] },
{ "from": "entities", "allow": ["shared"] },
{ "from": "shared", "allow": ["shared"] }
]
}]
}
}
Обратные ссылки
Автоссылки: где используется этот документ.
- frontend-design-system-rules.md (1): Правила Frontend Design System
- frontend-form-field-behavior-rules.md (1): Правила Поведения Полей В Формах-Редакторах
- frontend-state-management-rules.md (1): Правила Frontend State Management
- frontend-store-template.md (1):
Связанные документы
- frontend-rules.md
- frontend-design-system-rules.md
- frontend-state-management-rules.md
- frontend-documentation-rules.md
docs/04-frontends/pin-admin/00-conventions/— конкретика для приложения.- Полная документация HeroUI (компоненты, props, паттерны):
.claude/docs/heroui/llms-full.txt.