2026-04-03 12:37:08 +03:00
ФЛОУ
2026-04-03 19:40:15 +03:00
- после создания компонента, заменить шаблонный коментарий документа на реальный.
Проблема, неочевидность слоев (наследие FSD)
2026-04-03 19:54:46 +03:00
Архитектурные слои проекта
Каждый нижний слой не знает о существовании верхних. Импорты идут только сверху вниз.
pages → layouts → screens → widgets → features → entities → shared
---
1. Pages (pages/)
Точка входа маршрута. Только связывает layout и screen.
Правила:
- Никакой логики, стилей, разметки кроме композиции
- Один page = один layout + один screen
Пример:
// pages/knv-new.js
import { KnvScreen } from 'src/screens/knv'
import { MainLayout } from 'src/layouts/main'
const KnvNewPage = () => (
<MainLayout>
<KnvScreen />
</MainLayout>
)
---
2. Layouts (src/layouts/)
Каркас страницы — общие элементы, которые одинаковы на всех страницах в рамках этого layout.
Содержит в ui/: header, footer, sidebar — дочерние компоненты, которые привязаны к layout и не переиспользуются отдельно.
Критерий: компонент одинаков на всех страницах, использующих этот layout? → layouts/{name}/ui/
Пример:
src/layouts/main/
├── main.layout.tsx # <Header /> + children + <Footer />
├── ui/
│ ├── header/ # всегда одинаковый на всех страницах
│ └── footer/ # всегда одинаковый на всех страницах
---
3. Screens (src/screens/)
Контент конкретной страницы. Собирает свои секции и переиспользуемые widgets/features/entities.
Содержит в ui/: блоки, которые существуют только на этой странице и не переиспользуются.
Критерий: компонент используется только на одной странице? → screens/{name}/ui/
Пример:
src/screens/knv/
├── knv.screen.tsx
├── ui/
│ ├── hero-section/ # hero только на главной К Н В
│ ├── products-section/ # секция препаратов только на главной
│ ├── diseases-section/ # секция заболеваний только на главной
│ └── doctor-section/ # секция врачей только на главной
Каждая секция внутри может использовать shared/ui компоненты:
// screens/knv/ui/products-section/products-section.widget.tsx
import { Carousel } from 'src/shared/ui/carousel'
import { ProductCard } from './ui/product-card' // локальный, пока не переиспользуется
Когда локальный компонент начинает использоваться на 2+ страницах — выносим в entities/ или shared/ui.
---
4. Widgets (src/widgets/)
Составные блоки с данными/логикой, которые переиспользуются на 2+ страницах.
Критерий: блок с бизнес-логикой + данными используется на нескольких страницах? → widgets/
Пример: Слайдер «Популярные препараты» с загрузкой данных из API, который показывается и на главной, и на странице заболевания, и в каталоге:
src/widgets/
├── popular-products-slider/
│ ├── popular-products-slider.widget.tsx # Carousel + ProductCard + useProducts()
│ ├── hooks/
│ │ └── use-products.hook.ts # запрос данных
Н е widget: секция «Подобрать врача» которая есть только на главной → screens/knv/ui/
---
5. Features (src/features/)
Пользовательское действие или интерактивный сценарий. Содержит бизнес-логику взаимодействия.
Критерий: это действие пользователя (отправить форму, авторизоваться, добавить в корзину)? → features/
Примеры:
src/features/
├── auth/ # авторизация (форма + логика + стор)
│ ├── auth.feature.tsx
│ ├── hooks/
│ │ └── use-auth.hook.ts
│ └── stores/
│ └── auth.store.ts
│
├── order-drug/ # заказ препарата (кнопка + модалка + API)
│ ├── order-drug.feature.tsx
│ └── hooks/
│ └── use-order.hook.ts
Н е feature: отображение карточки препарата без взаимодействия → entities/ или shared/ui
---
6. Entities (src/entities/)
Бизнес-сущность с её отображением и типами. Привязана к домену (препарат, заболевание, врач, пользователь).
Критерий: это представление бизнес-объекта, которое переиспользуется в разных контекстах? → entities/
Примеры:
src/entities/
├── product/ # Препарат
│ ├── ui/
│ │ └── product-card/ # карточка препарата (каталог, слайдеры, поиск)
│ ├── types/
│ │ └── product.type.ts # { id, name, mnn, indication }
│ └── index.ts
│
├── disease/ # Заболевание
│ ├── ui/
│ │ └── disease-card/
│ ├── types/
│ │ └── disease.type.ts
│ └── index.ts
Отличие от shared/ui: entity-компонент знает о бизнес-домене (принимает Product, а не абстрактные пропсы). shared/ui Button не знает ничего о бизнесе.
---
7. Shared (src/shared/)
Переиспользуемые компоненты, утилиты, стили без бизнес-логики.
Критерий: компонент не знает о бизнес-домене, работает с абстрактными данными? → shared/
src/shared/
├── ui/ # UI-компоненты
│ ├── carousel/ # принимает children, не знает о препаратах
│ ├── container/
│ ├── section/
│ └── icon-svg/
├── styles/ # CSS-переменные, media
│ ├── variables.css
│ └── media.css
├── sprites/ # SVG-спрайты
└── lib/ # утилиты, хелперы
---
Сводная таблица принятия решений
Вопрос Да → Нет ↓
Это точка входа маршрута? pages/ ↓
Одинаков на всех страницах layout? layouts/{name}/ui/ ↓
Используется только на одной странице? screens/{name}/ui/ ↓
Составной блок с данными на 2+ страницах? widgets/ ↓
Это действие пользователя с логикой? features/ ↓
Привязан к бизнес-сущности? entities/ ↓
2026-04-03 20:02:01 +03:00
Абстрактный UI без бизнес-логики? shared/ui/ —
И еще размышлений
Что у нас по факту
Мы взяли FSD и добавили:
- Слой Layouts — каркас страницы
- Слой Screens — контент страницы с локальными компонентами
- Правило scope — компонент живёт максимально близко к месту использования, и поднимается на уровень выше только при переиспользовании
Это не FSD в чистом виде. Н о и не что-то принципиально новое.
Существующие названия
Feature-Sliced Design (FSD) — то от чего отталкиваемся, но расходимся в деталях.
Screaming Architecture (Robert C. Martin) — структура проекта "кричит" о том, что он делает. Открываешь screens/knv/ui/ — видишь К Н В . Открываешь widgets/ — видишь глобальное. Это близко к нашему подходу.
Colocation Principle — держи код рядом с тем, где он используется. Именно этот принцип отличает нас от FSD: page-specific компоненты лежат внутри screen, а не в глобальном widgets/.
Как назвать
Я бы не стал придумывать новое название. Это:
> FSD с принципом колокации (FSD + Colocation)
Или проще — адаптированный FSD. Суть отличия укладывается в одно правило:
> Компонент живёт на самом низком уровне, где он используется. Поднимается выше только при переиспользовании на 2+ страницах.
Если хочется краткое название для внутренней документации — можно SLD (Scoped Layer Design), подчёркивая что каждый компонент привязан к scope (shared → entity → feature → widget → screen → layout → page).