From 36304c14f0a29cd40cfd6181e71256d41dba81b1 Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Mon, 20 Apr 2026 10:40:52 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D1=8B=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B0=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D1=83=D1=80=D1=83=20SLM=20Design?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - заменены все упоминания FSD на SLM Design в 10 файлах документации - обновлены слои features/ и entities/ на business/, infrastructure/, ui/ - обновлены таблицы генерации кода и CLI-примеры - обновлены примеры путей и деревья файлов (naming, project-structure) - исправлен YAML frontmatter в architecture.md (двоеточие без кавычек) - перегенерированы RULES.md и README --- README_RU.md | 6 +- docs/ru/applied/project-structure.md | 18 +- docs/ru/applied/templates-generation.md | 5 +- docs/ru/basics/architecture.md | 890 +++++++++++++--------- docs/ru/basics/naming.md | 8 +- docs/ru/basics/tech-stack.md | 2 +- docs/ru/index.md | 6 +- docs/ru/workflow.md | 17 +- docs/ru/workflow/code-generation.md | 7 +- docs/ru/workflow/creating-app.md | 4 +- docs/ru/workflow/creating-components.md | 2 +- docs/ru/workflow/getting-started.md | 4 +- generated/ru/RULES.md | 945 ++++++++++++++---------- 13 files changed, 1142 insertions(+), 772 deletions(-) diff --git a/README_RU.md b/README_RU.md index 0ead4f8..1c3f438 100644 --- a/README_RU.md +++ b/README_RU.md @@ -18,7 +18,7 @@ | Создание проекта | Как начать новый проект? | | Генерация кода | Какие модули должны генерироваться из шаблонов? | | Добавление страницы | Как добавить новую страницу в проект? | -| Добавление UI-модуля | Как создать компонент, фичу, виджет, сущность или layout? | +| Добавление UI-модуля | Как создать компонент, бизнес-модуль, виджет или layout? | | Стилизация | Как стилизовать компоненты в проекте? | | Получение данных | Как получать данные с сервера? | | Управление состоянием | Как работать с состоянием? | @@ -31,7 +31,7 @@ | Раздел | Отвечает на вопрос | |--------|-------------------| | Технологии и библиотеки | Какой стек используем? | -| Архитектура | Как устроены слои FSD, зависимости, публичный API? | +| Архитектура | Как устроены слои SLM, зависимости, публичный API? | | Стиль кода | Как оформлять код: отступы, кавычки, импорты, early return? | | Именование | Как называть файлы, переменные, компоненты, хуки? | | Документирование | Как писать JSDoc: что документировать, а что нет? | @@ -44,7 +44,7 @@ | Раздел | Отвечает на вопрос | |--------|-------------------| | Настройка VS Code | Как настроить редактор для проекта? | -| Структура проекта | Как организованы папки и файлы по FSD? | +| Структура проекта | Как организованы папки и файлы по SLM? | | Компоненты | Как устроен компонент: файлы, пропсы, clsx? | | Page-level компоненты | Как описывать layout, page, loading, error, not-found? | | Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? | diff --git a/docs/ru/applied/project-structure.md b/docs/ru/applied/project-structure.md index fbd63ca..64463b8 100644 --- a/docs/ru/applied/project-structure.md +++ b/docs/ru/applied/project-structure.md @@ -41,19 +41,20 @@ public/ ```text src/ ├── app/ # Роутинг Next.js, провайдеры, глобальные стили -├── screens/ # Собраные страницы (UI) -├── layouts/ # Шаблоны -├── widgets/ # Крупные самостоятельные блоки интерфейса -├── features/ # Пользовательские сценарии -├── entities/ # Бизнес-сущности -└── shared/ # Переиспользуемый код (UI, утилиты, типы и др.) +├── layouts/ # Каркасы страниц (header, footer, sidebar) +├── screens/ # Контент конкретной страницы +├── widgets/ # Составные блоки интерфейса, не привязанные к домену +├── business/ # Бизнес-домены (auth, catalog, orders) +├── infrastructure/ # Техсервисы (theme, i18n, API-адаптеры) +├── ui/ # UI-кит без бизнес-логики +└── shared/ # Общие ресурсы (утилиты, типы, стили) ``` Принципы организации слоёв описаны в разделе [Архитектура](../basics/architecture). ### Папка `app/` -Совмещает два слоя: инициализацию приложения по FSD (провайдеры, глобальные стили) и файловый роутинг Next.js (`layout.tsx`, `page.tsx`, route-сегменты). +Точка входа приложения: инициализация (провайдеры, глобальные стили) и файловый роутинг Next.js (`layout.tsx`, `page.tsx`, route-сегменты). ```text src/app/ @@ -73,8 +74,7 @@ src/app/ ├── screen/ # Шаблон экрана ├── layout/ # Шаблон layout ├── widget/ # Шаблон виджета -├── feature/ # Шаблон фичи -├── entity/ # Шаблон сущности +├── module/ # Шаблон бизнес-модуля └── store/ # Шаблон стора ``` diff --git a/docs/ru/applied/templates-generation.md b/docs/ru/applied/templates-generation.md index 60f4108..0344a86 100644 --- a/docs/ru/applied/templates-generation.md +++ b/docs/ru/applied/templates-generation.md @@ -146,11 +146,10 @@ npx @gromlab/create <шаблон> <имя> <путь> | Команда | Что создаёт | |---|---| | `npx @gromlab/create component button src/shared/ui` | Компонент | -| `npx @gromlab/create feature auth src/features` | Фичу | +| `npx @gromlab/create module auth src/business` | Бизнес-модуль | | `npx @gromlab/create widget header src/widgets` | Виджет | -| `npx @gromlab/create entity user src/entities` | Сущность | | `npx @gromlab/create layout admin src/layouts` | Layout | -| `npx @gromlab/create store auth src/shared/model` | Стор | +| `npx @gromlab/create store auth src/business/auth/stores` | Стор | ::: diff --git a/docs/ru/basics/architecture.md b/docs/ru/basics/architecture.md index 2a58206..40b4ca9 100644 --- a/docs/ru/basics/architecture.md +++ b/docs/ru/basics/architecture.md @@ -1,471 +1,659 @@ --- title: Архитектура +description: "Раздел описывает архитектуру проекта: из каких слоёв состоит приложение, как организован код внутри слоёв и какие правила управляют зависимостями." --- -# Архитектура +# SLM Design +Scoped Layered Module Design — модульная архитектура фронтенд-приложений. Код организован по слоям ответственности, а модуль содержит всё, что ему нужно: компоненты, хуки, сторы, типы, стили. -Раздел описывает архитектуру проекта: из каких слоёв состоит приложение, -как организован код внутри слоёв и какие правила управляют зависимостями. +## Преимущества -## Что нужно знать +### Вертикальная организация домена -SLM Design (Scoped Layered Module Design) — архитектурный подход -к проектированию фронтенд-приложений, предложенный Громовым Сергеем в 2026 г. +Бизнес-домен не разбивается по техническим слоям — сценарии, сущности, типы и UI живут в одном модуле. Это сокращает время навигации и упрощает сопровождение: все изменения домена локализованы. -Вырос на основе: +### Разделение ответственности без перегрузки слоёв -- [Feature-Sliced Design](https://feature-sliced.design) — слои и направление зависимостей -- Screaming Architecture — структура говорит сама за себя -- Colocation Principle — код рядом с местом использования +Сервисы приложения (`infrastructure/`), UI-кит (`ui/`) и общие ресурсы (`shared/`) — три разных слоя с разной природой. Ни один слой не превращается в свалку разнородного кода. -Переосмыслив эти подходы, SLM Design отличается от FSD в трёх аспектах: -где живёт код (колокация), как он организован (модули) -и как масштабируется (подъём при переиспользовании). +### Горизонтальная инкапсуляция -## Терминология +Вложенные модули (`parts/`) и направление зависимостей позволяют нескольким разработчикам работать над одной областью приложения параллельно, не затрагивая код друг друга. -Архитектура оперирует четырьмя ключевыми понятиями: +### Колокация по умолчанию -- **Слой** — содержит модули -- **Модуль** — содержит сегменты -- **Компонент** — содержит сегменты -- **Сегмент** — папка внутри модуля или компонента, группирующая код по назначению: UI-элементы (`ui/`), хуки (`hooks/`), типы (`types/`), стили (`styles/`) и другие +Код начинает жизнь рядом с местом использования и поднимается в общие слои только при реальной потребности. Глобальные слои не засоряются преждевременными абстракциями. -Модуль и компонент устроены одинаково — оба имеют сегменты. Разница в том, где они живут и обязателен ли UI. +### Явное разделение каркаса и контента + +Каркас группы маршрутов (`layouts/`) и контент конкретной страницы (`screens/`) — независимые слои с собственной ответственностью. + +### Масштабирование через группировку + +При росте проекта слои не теряют структуру — модули группируются по естественным признакам: бизнес-домены по субдоменам, страницы по разделам, UI-компоненты по уровню абстракции (примитивы и композиции). + +### Dependency Injection без фреймворков + +Cross-domain зависимости в бизнес-слое реализуются через фабрики — модуль декларирует что ему нужно, а точка использования предоставляет зависимости. Домены изолированы без DI-контейнеров, провайдеров и шин событий. + +## Происхождение + +SLM Design вырос на основе: + +- **Feature-Sliced Design** — слоистая структура, публичный API модуля, направление зависимостей +- **Vertical Slice Architecture** — модуль как вертикальный срез, содержащий всё необходимое +- **Screaming Architecture** — структура проекта «кричит» о назначении: открыл `business/auth` — видишь авторизацию +- **Colocation Principle** — код живёт рядом с местом использования + +## Пример структуры проекта ```text -Слой -└── Модуль - ├── Сегменты (hooks/, stores/, types/, styles/, lib/...) - └── ui/ - └── Компонент - ├── Сегменты (hooks/, stores/, types/, styles/, lib/...) - └── ui/ - └── Компонент → ... +src/ +├── app/ +│ +├── layouts/ +│ ├── main/ +│ └── dashboard/ +│ +├── screens/ +│ ├── home/ +│ ├── products/ +│ ├── product-detail/ +│ └── about/ +│ +├── widgets/ +│ ├── page-heading/ +│ ├── hero-section/ +│ └── promo-banner/ +│ +├── business/ +│ ├── auth/ +│ ├── catalog/ +│ ├── orders/ +│ └── chat/ +│ +├── infrastructure/ +│ ├── theme/ +│ ├── i18n/ +│ ├── backend-api/ +│ └── logger/ +│ +├── ui/ +│ ├── button/ +│ ├── input/ +│ ├── modal/ +│ ├── toast/ +│ └── dropdown/ +│ +└── shared/ + ├── lib/ + ├── types/ + └── styles/ ``` -### Слой +## Принципы -Архитектурный уровень. Содержит только модули. Определяет назначение кода и правила зависимостей. - -### Модуль - -Единица первого уровня слоя, объединённая по смыслу. Может содержать компонент, логику, типы, стили — или любую комбинацию. Имеет публичный API (`index.ts`) и внутреннюю структуру из сегментов. - -Модуль — не обязательно UI. Feature `analytics` может быть только стором и сервисом. Entity `session` может быть только типами и хуком. - -Модуль не может содержать вложенных модулей. Вложенные единицы с UI размещаются в сегменте `ui/` как компоненты. - -### Компонент - -Вложенная единица внутри сегмента `ui/` модуля (или другого компонента). Публичный `.tsx` файл обязателен. Именуется без суффикса слоя. - -Компонент может иметь собственные сегменты (`hooks/`, `styles/`, `types/` и т.д.), `index.ts` и свой `ui/` с ещё более вложенными компонентами. - -Отличия от модуля: - -| | Модуль | Компонент | -|--|--------|-----------| -| Где живёт | В корне слоя | В `ui/` модуля или другого компонента | -| Публичный `.tsx` | С суффиксом слоя, опционален | Без суффикса, обязателен | -| Может не иметь UI | Да | Нет | - -Пример: - -```text -features/auth-by-email/ # модуль -├── auth-by-email.feature.tsx # публичный .tsx модуля (с суффиксом, опционален) -├── ui/ # сегмент: компоненты -│ ├── login-form/ # компонент -│ │ ├── login-form.tsx # публичный .tsx компонента (без суффикса, обязателен) -│ │ ├── ui/ # вложенные компоненты -│ │ │ └── password-field/ -│ │ │ └── password-field.tsx -│ │ ├── hooks/ -│ │ │ └── use-validation.hook.ts -│ │ ├── styles/ -│ │ │ └── login-form.module.css -│ │ └── index.ts -│ └── reset-password/ # компонент -│ ├── reset-password.tsx -│ └── index.ts -├── hooks/ -│ └── use-auth.hook.ts -├── stores/ -│ └── auth.store.ts -└── index.ts -``` - -### Сегмент - -Техническая папка внутри модуля или компонента, группирующая код по назначению. Набор не фиксирован — включаются только те сегменты, которые нужны. - -| Сегмент | Назначение | -|---------|-----------| -| `ui/` | Вложенные компоненты | -| `hooks/` | React-хуки | -| `stores/` | Сторы состояния | -| `types/` | Интерфейсы, типы, enums, DTO | -| `styles/` | Стили | -| `lib/` | Утилиты | -| `services/` | Внешние источники данных | -| `helpers/` | Вспомогательные функции | -| `config/` | Константы, конфигурация | - -## Ключевой принцип - -> Модуль живёт на самом низком уровне, где он используется. -> Поднимается выше только при переиспользовании в 2+ местах. - -Если модуль используется только в одном месте — он остаётся на текущем уровне. -Как только он начинает использоваться в 2+ местах — выносится на уровень выше. -В крайнем случае — в `shared/`, где он доступен всем. +- **Домен — единое целое.** Всё, что относится к домену, живёт в одном модуле. +- **Колокация.** Код рождается рядом с местом использования и поднимается только при необходимости. +- **Зависимости однонаправлены.** Импорты только сверху вниз, только через публичный API. +- **Архитектура — каркас, не клетка.** Правила фиксируют направление зависимостей и структуру модуля, остальное определяет команда. ## Слои -Каждый нижний слой не знает о существовании верхних. Импорты идут строго сверху вниз. +Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом. + +### Определение + +**Слой — уровень организации кода внутри `src/`. Каждый слой отвечает за свою область (каркас страницы, бизнес-логика, UI-кит) и задаёт правила для кода внутри: направление импортов, именование, допустимые связи между модулями.** + +### Группы слоёв + +Слои делятся на три группы: + +| Группа | Слои | Описание | +|--------|------|----------| +| Композиция | `app`, `layouts`, `screens`, `widgets` | Собирают интерфейс из готовых блоков: маршруты, каркасы, страницы | +| Ядро | `business`, `infrastructure`, `ui` | Реализация продукта: бизнес-домены, техсервисы, UI-кит | +| Фундамент | `shared` | Общие ресурсы: утилиты, хелперы, стили, конфиги | + +### Направление зависимостей + +Любой импорт между модулями — только через публичный API. ``` -app → layouts → screens → widgets → features → entities → shared +app → [ layouts | screens ] → widgets → business → infrastructure → ui → shared ``` -| Слой | Что лежит | Импортирует | -|------|-----------|-------------| -| **App** | Роутинг, провайдеры, глобальные стили. Композиция layout + screen для маршрута. | Все слои ниже | -| **Layouts** | Каркас страницы, общий для группы маршрутов. | widgets, features, entities, shared | -| **Screens** | Контент конкретной страницы. | widgets, features, entities, shared | -| **Widgets** | Составные блоки с данными/логикой, переиспользуемые в 2+ местах. | features, entities, shared | -| **Features** | Пользовательское действие или интерактивный сценарий. | entities, shared | -| **Entities** | Бизнес-сущность с отображением и типами. | shared | -| **Shared** | Переиспользуемые компоненты, утилиты, стили без бизнес-логики. | ничего | +- `layouts` и `screens` — параллельные слои, не импортируют друг друга +- Модули одного слоя в группе «Композиция» изолированы друг от друга +- Модули одного слоя `infrastructure` и `ui` могут импортировать друг друга через публичный API +- Модули `business` — cross-domain зависимости по коду через фабрику, `import type` напрямую +- Импорт типов (`import type`) в «Ядре» разрешён в обоих направлениях -Принципы: -- Импорты строго сверху вниз -- Модули одного слоя не знают друг о друге -- Layout получает контекстно-зависимые блоки через пропсы от app, а не импортирует их сам -- `entities/` и `features/` создаются осознанно — это не результат «вынесения» компонента из screen +### Слой App -### App +Точка входа приложения. Отвечает за запуск, роутинг и композицию маршрутов из layout и screen. -Точка входа приложения: роутинг (Next.js App Router), провайдеры, глобальные стили. -Находится на самом высоком уровне абстракции — может импортировать любой слой ниже. -Никакой бизнес-логики — только композиция. +В отличие от остальных слоёв, `app/` не содержит модулей SLM. Здесь живут только инфраструктурные файлы, которые не могут быть никаким другим слоем: файлы фреймворка роутинга, точка запуска и код инициализации. -```text -app/ -├── layout.tsx # RootLayout: провайдеры, глобальные стили -├── page.tsx # Главная: MainLayout + HomeScreen -├── knv-new/ -│ └── page.tsx # КНВ: MainLayout + KnvScreen -└── catalog/ - └── page.tsx # Каталог: MainLayout + CatalogScreen -``` +#### Требования -```tsx -// app/knv-new/page.tsx -import { MainLayout } from '@/layouts/main' -import { KnvScreen } from '@/screens/knv' +- Не содержит модулей SLM — только файлы фреймворка, роутинг, инициализация +- Содержит: файлы маршрутов, bootstrap, обработку ошибок верхнего уровня (404, error boundary), подключение глобальных стилей и ассетов +- Провайдеры и гарды — только подключает готовые из нижних слоёв, не реализует +- Не содержит бизнес-логику, UI-компоненты, хуки, сторы, сервисы +- Никем не импортируется -export default function KnvNewPage() { - return ( - - - - ) -} -``` +### Слой Layouts -Если layout требует разный контент в зависимости от страницы — app передаёт его через пропсы: - -```tsx -// app/knv-new/page.tsx -import { MainLayout } from '@/layouts/main' -import { KnvScreen } from '@/screens/knv' -import { KnvHeader } from '@/widgets/knv-header' - -export default function KnvNewPage() { - return ( - }> - - - ) -} -``` - -### Layouts - -Каркас страницы — общие элементы, одинаковые для группы маршрутов (header, footer, sidebar). - -Если компонент внутри layout начинает использоваться в 2+ местах — он выносится в `widgets/` или `shared/ui/`. +Каркас страницы: общие элементы, одинаковые для группы маршрутов (header, footer, sidebar). ```text src/layouts/ -└── main/ - ├── main.layout.tsx - ├── ui/ - │ ├── header/ - │ │ └── header.tsx - │ └── footer/ - │ └── footer.tsx - └── index.ts +├── main/ +├── dashboard/ +└── auth/ ``` -### Screens +#### Требования -Контент конкретной страницы. Собирает локальные секции и переиспользуемые модули из нижних слоёв. +- Содержит только модули +- Не содержит бизнес-логику +- Контекстно-зависимые блоки принимает через пропсы от `app`, не импортирует напрямую -Если компонент внутри screen начинает использоваться в 2+ местах — он выносится в `widgets/` или `shared/ui/`. +### Слой Screens + +Контент конкретной страницы: собирает её из модулей нижних слоёв. ```text src/screens/ -└── knv/ - ├── knv.screen.tsx - ├── ui/ - │ ├── hero-section/ - │ │ └── hero-section.tsx - │ ├── products-section/ - │ │ └── products-section.tsx - │ └── diseases-section/ - │ └── diseases-section.tsx - └── index.ts +├── home/ +├── products/ +├── product-detail/ +├── about/ +└── contacts/ ``` -### Widgets +Когда количество страниц затрудняет навигацию — вводится группировка по разделам. Группа — папка для организации, не модуль (без `index.ts`). -Составные блоки с данными и логикой, переиспользуемые в 2+ местах. +```text +src/screens/ +├── shop/ +│ ├── home/ +│ ├── products/ +│ ├── product-detail/ +│ └── cart/ +├── account/ +│ ├── profile/ +│ ├── settings/ +│ └── order-history/ +└── info/ + ├── about/ + ├── contacts/ + └── faq/ +``` -Если блок с логикой нужен только в одном месте — это компонент внутри `screens/{name}/ui/` или `layouts/{name}/ui/`, а не widget. +#### Требования + +- Содержит только модули +- Не содержит бизнес-логику +- Локальные одноразовые секции живут внутри screen-модуля, не выносятся в `widgets`/`business` + +### Слой Widgets + +Составной блок интерфейса, который компонует модули ядра, но не принадлежит конкретному бизнес-домену. Widget появляется когда блок используется в нескольких screens или layouts. + +Если блок принадлежит домену — он живёт в `business/{area}/`, даже если переиспользуется. Если блок нужен только в одном месте — это `screens/{name}/parts/` или `layouts/{name}/parts/`, а не widget. ```text src/widgets/ -└── popular-products-slider/ - ├── popular-products-slider.widget.tsx - ├── ui/ - │ └── slider-card/ - │ └── slider-card.tsx - ├── hooks/ - │ └── use-products.hook.ts - └── index.ts +├── page-heading/ +├── hero-section/ +├── onboarding-checklist/ +├── promo-banner/ +└── error-boundary/ ``` -### Features +#### Требования -Пользовательское действие или интерактивный сценарий: авторизация, заказ, добавление в корзину. +- Не принадлежит конкретному бизнес-домену. Если блок доменный — он живёт в `business/` +- Используется в нескольких screens или layouts -Feature создаётся осознанно, когда есть действие пользователя с бизнес-логикой. Компонент опционален — feature может экспортировать хуки, сторы, компоненты или всё вместе. +### Слой Business + +Бизнес-домены приложения: auth, catalog, orders, checkout, chat. Каждый домен — отдельный модуль со своими типами, логикой, UI и сервисами. + +Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`. Cross-domain зависимости по коду реализуются через фабрику. `import type` между доменами разрешён напрямую. + +Business объединяет то, что в FSD разделено на `features` и `entities`: пользовательские сценарии и бизнес-сущности живут вместе, внутри одного домена. Внутри домена сегменты разделяют ответственность: `types/` — доменная модель, `hooks/` и `services/` — сценарии и логика, `mappers/` — трансформация данных, `parts/` — составные блоки. ```text -src/features/ -└── auth-by-email/ - ├── auth-by-email.feature.tsx - ├── ui/ - │ ├── login-form/ - │ │ └── login-form.tsx - │ └── reset-password/ - │ └── reset-password.tsx - ├── hooks/ - │ └── use-auth.hook.ts - ├── stores/ - │ └── auth.store.ts - └── index.ts +src/business/ +├── auth/ +├── catalog/ +├── orders/ +├── checkout/ +└── chat/ ``` -### Entities - -Бизнес-сущность с отображением и типами: препарат, заболевание, врач, пользователь. - -Entity создаётся осознанно, когда появляется бизнес-сущность. Компонент опционален — entity может быть только типами и хуком. - -Отличие от `shared/ui/`: entity-компонент знает о бизнес-домене (принимает `Product`, а не абстрактные пропсы). +Когда количество доменов затрудняет навигацию — вводится группировка по субдоменам. Группа — папка для организации, не модуль (без `index.ts`). ```text -src/entities/ -├── product/ -│ ├── product.entity.tsx -│ ├── ui/ -│ │ └── product-card/ -│ │ └── product-card.tsx -│ ├── types/ -│ │ └── product.type.ts -│ └── index.ts -│ -├── session/ -│ ├── types/ -│ │ └── session.type.ts -│ ├── hooks/ -│ │ └── use-session.hook.ts -│ └── index.ts +src/business/ +├── commerce/ +│ ├── catalog/ +│ ├── cart/ +│ ├── orders/ +│ └── checkout/ +└── communication/ + ├── chat/ + └── notifications/ ``` -### Shared +#### Требования -Переиспользуемые компоненты, утилиты, стили без бизнес-логики. Не знает о бизнес-домене — работает с абстрактными данными. +- Один модуль = один бизнес-домен +- Циклические зависимости между доменами запрещены +- Импорт кода между доменами — через фабрику. `import type` — напрямую +- Доменные типы (`User`, `Product`) живут здесь, не в `shared/` -Структурирован как набор сегментов: +### Слой Infrastructure + +Техсервисы приложения: theme, i18n, API-адаптеры, logger, realtime. Каждый сервис — отдельный модуль. + +Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`. + +Отличие от `shared/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги). + +```text +src/infrastructure/ +├── theme/ +├── i18n/ +├── backend-api/ +├── maps-api/ +├── logger/ +├── feature-flags/ +└── realtime/ +``` + +#### Требования + +- Один модуль = один техсервис +- Импортирует `infrastructure/`, `ui/`, `shared/` + +### Слой UI + +UI-кит без бизнес-логики: button, carousel, toast, modal. + +Слой входит в группу «Ядро». Импортирует `ui/` и `shared/`. + +Компоненты строятся друг на друге: `button` использует `icon`, `carousel` использует `button`. + +```text +src/ui/ +├── button/ +├── input/ +├── icon/ +├── carousel/ +├── modal/ +├── toast/ +├── dropdown/ +├── tabs/ +└── tooltip/ +``` + +Когда количество компонентов затрудняет навигацию — вводится группировка на примитивы и композиции. Примитивы (`button`, `icon`, `input`) не импортируют композиции. Композиции (`carousel`, `modal`, `dropdown`) строятся на примитивах. + +```text +src/ui/ +├── primitives/ +│ ├── button/ +│ ├── input/ +│ ├── icon/ +│ └── badge/ +└── composites/ + ├── carousel/ + ├── modal/ + ├── dropdown/ + ├── tabs/ + └── tooltip/ +``` + +#### Требования + +- Не содержит бизнес-логику +- Импортирует только `ui/` и `shared/` + +### Слой Shared + +Общие ресурсы: утилиты, хелперы, стили, конфиги. Не знает о бизнес-домене. + +Слой входит в группу «Фундамент» — ни о ком не знает, никого не импортирует. + +Отличие от `infrastructure/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги). + +Отличие от `ui/`: UI-компоненты (button, carousel, modal) живут в слое `ui/`, а не здесь. ```text src/shared/ -├── ui/ -│ ├── icon/ -│ │ └── icon.tsx -│ ├── carousel/ -│ │ ├── carousel.tsx -│ │ ├── ui/ -│ │ │ ├── carousel-slide/ -│ │ │ │ └── carousel-slide.tsx -│ │ │ └── carousel-dots/ -│ │ │ └── carousel-dots.tsx -│ │ └── index.ts -│ ├── container/ -│ └── button/ ├── lib/ -│ ├── format-date.ts -│ └── cn.ts +├── types/ ├── styles/ -│ ├── variables.css -│ └── media.css └── sprites/ ``` -## Модуль +#### Требования + +- Не имеет runtime-состояния + +## Модули + +Раздел описывает модули SLM: что такое модуль, из чего он состоит и как взаимодействует с остальным кодом. + +### Определение + +**Модуль — универсальный строительный блок архитектуры. Живёт на слое и содержит всё необходимое для своей работы: компоненты, хуки, сторы, сервисы, типы, стили. Набор содержимого не фиксирован — включаются только нужные части.** + +### Модуль vs компонент + +**Компонент** — один `.tsx` файл. Не имеет своих сегментов, использует сегменты родительского модуля. Живёт в корне или `ui/` сегменте модуля. + +**Модуль** — папка, которая может содержать корневой компонент, сегменты (`hooks/`, `types/`, `styles/`, `ui/`, `parts/` и т.д.) и публичный API (`index.ts`). + +```text +auth/ +├── ui/ +│ ├── auth-guard.tsx +│ └── logout-button.tsx +├── parts/ +│ ├── login-form/ +│ ├── registration-form/ +│ └── restore-form/ +├── hooks/ +├── stores/ +├── types/ +├── auth.tsx # корневой компонент (опционален) +└── index.ts +``` ### Структура -```text -{name}/ -├── {name}.{суффикс}.tsx # компонент (опционален) -├── ui/ # вложенные компоненты -├── hooks/ # хуки -├── stores/ # сторы -├── types/ # типы, интерфейсы, enums -├── styles/ # стили -├── lib/ # утилиты -├── services/ # внешние источники данных -├── helpers/ # вспомогательные функции -├── config/ # константы, конфигурация -└── index.ts # публичный API -``` - -### Именование компонента - -Суффикс слоя получают **только модули первого уровня слоя** — те, что лежат непосредственно в корне слоя. Все компоненты (в `ui/`, любой глубины) именуются без суффикса. Без исключений. - -| Слой | Суффикс | Пример | -|------|---------|--------| -| Layouts | `.layout.tsx` | `main.layout.tsx` | -| Screens | `.screen.tsx` | `knv.screen.tsx` | -| Widgets | `.widget.tsx` | `popular-products-slider.widget.tsx` | -| Features | `.feature.tsx` | `auth-by-email.feature.tsx` | -| Entities | `.entity.tsx` | `product.entity.tsx` | - -Примеры: +Модуль состоит из сегментов. Ни один сегмент не обязателен — модуль может состоять даже из одного `index.ts` с реэкспортом типов. ```text -features/auth-by-email/auth-by-email.feature.tsx # модуль первого уровня → суффикс -features/auth-by-email/ui/login-form/login-form.tsx # компонент в ui/ → без суффикса -shared/ui/carousel/carousel.tsx # компонент в shared → без суффикса +{module-name}/ +├── {module-name}.tsx # корневой компонент (опционален) +├── ui/ # компоненты модуля (только .tsx) +├── parts/ # вложенные модули (со своими сегментами) +├── hooks/ # хуки +├── stores/ # сторы состояния +├── services/ # внешние источники данных +├── mappers/ # трансформация данных между форматами +├── types/ # типы +├── styles/ # стили +├── lib/ # утилиты модуля +├── config/ # константы +└── index.ts # публичный API ``` -### Правила импорта +Подробное описание каждого сегмента — в разделе [Сегменты](/reference/segments). -Три уровня правил: +### Публичный API -**Между слоями** — импорты строго сверху вниз: +Модуль экспортирует наружу только то, что нужно другим. Всё остальное — внутреннее. -``` -app → layouts → screens → widgets → features → entities → shared +```ts +// business/auth/index.ts +export type { User, Session } from './types/user.types' +export { useAuth } from './hooks/use-auth.hook' +export { AuthGuard } from './ui/auth-guard' ``` -**Внутри модуля (не shared)** — сегменты доступны друг другу и компонентам. Компоненты внутри одного `ui/` не импортируют друг друга: +Импорт в обход `index.ts` запрещён: + +```ts +// Плохо +import { validateToken } from '@/business/auth/lib/tokens' + +// Хорошо +import { useAuth } from '@/business/auth' +``` + +### Фабрика + +Если модуль зависит от кода другого бизнес-домена — он экспортирует фабрику. Фабрика декларирует необходимые зависимости и возвращает API модуля. Точка использования (screen, widget, layout) предоставляет зависимости при вызове. + +Модуль без cross-domain зависимостей экспортирует API напрямую. Типы всегда экспортируются напрямую — `import type` не является runtime-зависимостью. + +#### Модуль без зависимостей — прямой экспорт: + +```ts +// business/auth/index.ts +export { useAuth } from './hooks/use-auth' +export { useCurrentUser } from './hooks/use-current-user' +export type { User, Session } from './types' +``` + +#### Модуль с зависимостями — фабрика: + +```ts +// business/chat/types/deps.ts +import type { User } from '@/business/auth' + +export interface ChatDeps { + useCurrentUser: () => User | null +} +``` + +```ts +// business/chat/index.ts +import type { ChatDeps } from './types/deps' + +export function chatFactory(deps: ChatDeps) { + return { + useMessages: (roomId: string) => { + const user = deps.useCurrentUser() + // ... + }, + useSendMessage: (roomId: string) => { + const user = deps.useCurrentUser() + return (text: string) => { /* ... */ } + }, + useChatRooms: () => { + const user = deps.useCurrentUser() + // ... + }, + ChatBadge: ({ count }: { count: number }) => { /* ... */ }, + } +} + +export type { Message, ChatRoom } from './types' +export type { ChatDeps } from './types/deps' +``` + +#### Использование на странице: + +```tsx +// screens/support/support.tsx +import { useCurrentUser } from '@/business/auth' +import { chatFactory } from '@/business/chat' + +const chat = chatFactory({ useCurrentUser }) + +export function SupportScreen() { + const { useMessages, useSendMessage, ChatBadge } = chat + const messages = useMessages('support') + const sendMessage = useSendMessage('support') + + return ( +
+ + {messages.map(m => )} + +
+ ) +} +``` + +### Жизненный цикл + +Модуль рождается на самом низком уровне использования и поднимается выше только при реальной потребности. + +- Нужен на одной странице → `screens/{name}/parts/` +- Появился в 2+ местах → поднимается по природе: + - абстрактный UI → `ui/` + - блок с данными/логикой → `widgets/` + - представление бизнес-домена → `business/{area}/parts/` + +Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность. + +## Сегменты + +Раздел описывает сегменты SLM: что такое сегмент, какие бывают и что в каждом из них лежит. + +### Определение + +**Сегмент — папка внутри модуля, которая группирует файлы по назначению. Набор сегментов не фиксирован — модуль включает только те, которые ему нужны. Команда сама определяет какие сегменты используются в проекте — архитектура даёт рекомендацию.** + +### Обзор + +| Сегмент | Содержимое | +|---------|------------| +| `ui/` | Компоненты модуля — только `.tsx` файлы | +| `parts/` | Вложенные модули со своими сегментами | +| `hooks/` | React-хуки | +| `stores/` | Сторы состояния | +| `services/` | Работа с внешними источниками данных | +| `mappers/` | Трансформация данных между форматами | +| `types/` | TypeScript-типы и интерфейсы | +| `styles/` | Стили | +| `lib/` | Утилиты и хелперы модуля | +| `config/` | Константы и конфигурация | + +### Сегмент ui/ + +Компоненты, принадлежащие модулю. Содержит только `.tsx` файлы — без своих сегментов, стилей, типов, хуков. Использует сегменты родительского модуля. ```text -features/auth-by-email/ +auth/ ├── ui/ -│ ├── login-form/ # НЕ может импортировать reset-password -│ └── reset-password/ # НЕ может импортировать login-form +│ ├── auth-provider.tsx +│ ├── auth-guard.tsx +│ └── logout-button.tsx +├── types/ +├── hooks/ +└── index.ts ``` -Если двум компонентам нужен общий код — он поднимается на уровень выше: +Если компоненту нужны собственные сегменты — это уже не `ui/`, а `parts/`. + +### Сегмент parts/ + +Вложенные модули со своими сегментами. Каждый элемент `parts/` — полноценный модуль: папка с компонентом, хуками, стилями, типами и т.д. ```text -features/auth/ui/login-form/ui/email-input/ # нужен соседу -→ features/auth/ui/email-input/ # поднимаем на уровень -→ shared/ui/email-input/ # если нужен за пределами фичи +home/ +├── parts/ +│ ├── hero-section/ +│ │ ├── hero-section.tsx +│ │ ├── styles/ +│ │ └── parts/ +│ │ └── top-banner/ +│ │ └── top-banner.tsx +│ └── features-section/ +│ ├── features-section.tsx +│ └── hooks/ +├── home.screen.tsx +└── index.ts ``` -Компоненты наследуют правила зависимостей **родительского слоя**: +Отличие от `ui/`: элемент `parts/` — модуль со своими сегментами. Элемент `ui/` — компонент, один `.tsx` файл. -- Компонент внутри `features/auth/ui/login-form/` может импортировать `entities/` и `shared/` — как и сам feature -- Компонент внутри `widgets/hero/ui/hero-stats/` может импортировать `features/`, `entities/`, `shared/` — как и сам widget +Вложенность `parts/` инкапсулирует область разработки горизонтально: каждый разработчик работает в своём `parts/`-модуле, не затрагивая чужие. Это снижает конфликты при параллельной разработке. -**Shared** — без ограничений на внутренние импорты. Компоненты в `shared/ui/` могут импортировать друг друга (`button` использует `icon`), `ui/` может использовать `lib/` и другие сегменты. Shared — фундамент, его компоненты строятся друг на друге. +Если вложенный модуль обрастает своими `parts/` — это сигнал, что он достаточно самостоятельный для подъёма на уровень выше. -Правила импорта между слоями enforceable через ESLint — настройка границ слоёв и запрет обратных зависимостей. +### Сегмент hooks/ -## Жизненный цикл модуля +React-хуки модуля. Инкапсулируют логику, состояние, подписки, побочные эффекты. -Модуль не проектируется «на вырост». Он рождается на самом низком уровне -и поднимается только когда появляется реальная потребность. +```text +hooks/ +├── use-auth.hook.ts +├── use-session.hook.ts +└── use-permissions.hook.ts +``` -Пример пути компонента `product-card`: +### Сегмент stores/ -1. **Начало:** `screens/catalog/ui/product-card/` — нужен только на странице каталога. -2. **Переиспользование:** появился на странице поиска — выносим выше. - Куда именно зависит от природы: - - Составной блок с данными и логикой → `widgets/product-card/` - - Представление бизнес-сущности → `entities/product/ui/product-card/` - - Абстрактный UI без бизнес-логики → `shared/ui/product-card/` +Сторы состояния модуля. Конкретная реализация зависит от выбранного стейт-менеджера (Zustand, MobX, Redux и т.д.). -Основной триггер подъёма — переиспользование в 2+ местах. -Но если очевидно что модуль будет переиспользоваться — разумно разместить его на нужном уровне сразу. +```text +stores/ +├── auth.store.ts +└── session.store.ts +``` -Как происходит подъём на практике: +### Сегмент services/ -1. Разработчик обнаруживает что компонент нужен в другом месте -2. Определяет целевой слой по природе компонента (widget / entity / shared) -3. Перемещает папку, обновляет импорты, добавляет суффикс если это модуль первого уровня слоя -4. Ревью подтверждает что подъём обоснован +Работа с внешними источниками данных: API-вызовы, запросы, подписки. -Подъём — это обычный рефакторинг в рамках задачи, а не отдельная активность. +```text +services/ +├── auth.service.ts +└── token.service.ts +``` -## Граничные случаи +### Сегмент mappers/ -| Ситуация | Решение | Почему | -|----------|---------|--------| -| Фильтр каталога только на одной странице, но с хуками и стором | Модуль в `screens/catalog/` с сегментами `hooks/`, `stores/` | Не feature — feature это действие пользователя с бизнес-логикой (авторизация, заказ), а не UI с состоянием | -| Компонент используется в 2 местах на одной странице | Остаётся в `screens/{name}/ui/` | Переиспользование внутри одного screen — не повод выносить в widget | -| Entity без UI (только типы и хук) | Нормально | Модуль не обязан иметь UI. `entities/session/` с типами и хуком — валидный модуль | -| Компонент нужен и в layout, и в screen | Выносить в `widgets/` или `shared/ui/` | Два разных слоя используют один компонент → переиспользование → подъём | -| Модуль screen содержит бизнес-логику (хуки, стор, сервисы) | Нормально | Это не значит что он должен стать feature. Модуль с логикой внутри screen — обычное дело, пока он не переиспользуется | -| Компонент в `shared/ui/button` хочет использовать `shared/ui/icon` | Разрешено | Shared — исключение: внутренние импорты без ограничений. Это фундамент, его компоненты строятся друг на друге | +Функции трансформации данных из одного формата в другой: DTO в доменный тип, доменный тип в DTO, доменный тип в ViewModel. -## Запрещено +```text +mappers/ +├── map-user.ts +├── map-product.ts +└── map-order-to-dto.ts +``` -- **Не создавать feature без бизнес-логики** — кнопка без состояния и сайд-эффектов это компонент в `ui/`, а не feature -- **Не класть доменные типы в shared** — если тип знает о Product, Disease, User — он живёт в `entities/`, не в `shared/` -- **Не создавать entity или feature как результат «вынесения»** — они создаются осознанно: появилась бизнес-сущность → entity, появилось действие пользователя → feature -- **Не импортировать соседние компоненты в `ui/` (кроме shared)** — если двум компонентам нужен общий код, он поднимается на уровень выше -- **Не хранить в shared «помойку для всего»** — shared содержит переиспользуемые компоненты и утилиты без бизнес-логики, а не код который «непонятно куда положить» +### Сегмент types/ -## Почему так, а не иначе +TypeScript-типы и интерфейсы модуля. Доменные типы, DTO, пропсы компонентов. -### Почему `ui/` а не `modules/` +```text +types/ +├── user.type.ts +└── session.type.ts +``` -Внутри сегмента `ui/` всегда лежат единицы с обязательным UI (компоненты). Название точно отражает содержимое. `modules/` был нейтральнее, но скрывал природу вложенных единиц и создавал путаницу — модуль внутри модуля размывал понятие «модуль как единица слоя». +### Сегмент styles/ -### Почему модуль и компонент — разные понятия +Стили модуля. Формат зависит от выбранного подхода (CSS Modules, SCSS, CSS-in-JS и т.д.). -Модуль — единица первого уровня слоя, может не иметь UI. Компонент — вложенная единица с обязательным UI. Разделение снимает вопрос «а если нет `.tsx` — это всё ещё компонент?» и делает название сегмента `ui/` честным. +```text +styles/ +├── auth.module.css +└── login-form.module.css +``` -### Почему shared без ограничений на внутренние импорты +### Сегмент lib/ -Shared — фундамент. Его компоненты строятся друг на друге: `button` использует `icon`, `carousel` использует `button`. Запрещать это — бороться с реальностью. Поднимать некуда — shared уже нижний слой. +Утилиты и хелперы, специфичные для модуля. Чистые функции без побочных эффектов. -### Почему нет слоя pages +```text +lib/ +├── validate-email.ts +└── format-phone.ts +``` -Роутинг живёт в `app/` (Next.js App Router). Отдельный слой `pages` конфликтовал бы с файловой структурой Next.js и дублировал ответственность `app/`. +Отличие от `shared/lib/`: здесь лежат утилиты, нужные только этому модулю. Общие утилиты — в `shared/lib/`. -### Почему компоненты в одном `ui/` не импортируют друг друга (кроме shared) +### Сегмент config/ -Независимые компоненты легко выносить в другой слой при переиспользовании. Если компонент A зависит от соседа B — при подъёме A придётся тянуть B. Это усложняет рефакторинг и нарушает принцип колокации. +Константы и конфигурация модуля: маршруты, лимиты, дефолтные значения. + +```text +config/ +├── routes.ts +└── constants.ts +``` diff --git a/docs/ru/basics/naming.md b/docs/ru/basics/naming.md index e2d7867..a7c37fb 100644 --- a/docs/ru/basics/naming.md +++ b/docs/ru/basics/naming.md @@ -53,7 +53,7 @@ title: Именование **Хорошо** ```text -features/ +business/ └── auth-by-email/ ├── ui/ │ └── login-form.tsx @@ -62,14 +62,14 @@ features/ ├── stores/ │ └── auth.store.ts ├── types/ - │ └── auth.interface.ts - ├── auth-by-email.feature.tsx + │ └── auth.type.ts + ├── auth-by-email.tsx └── index.ts ``` **Плохо** ```text -features/ +business/ └── authByEmail/ ├── LoginForm.tsx ├── useAuth.ts diff --git a/docs/ru/basics/tech-stack.md b/docs/ru/basics/tech-stack.md index 0092c07..40e8d8c 100644 --- a/docs/ru/basics/tech-stack.md +++ b/docs/ru/basics/tech-stack.md @@ -13,7 +13,7 @@ title: Технологии и библиотеки - `Next.js` — для продуктовых сайтов. ### Архитектура -- `FSD (Feature-Sliced Design)` — структура проекта и границы модулей. Используется кастомизированная версия — подробнее в разделе [Архитектура](/basics/architecture). +- `SLM Design` — собственная модульная архитектура проекта. Подробнее в разделе [Архитектура](/basics/architecture). ### UI компоненты - `Mantine UI` — базовые UI-компоненты. diff --git a/docs/ru/index.md b/docs/ru/index.md index 0ead4f8..1c3f438 100644 --- a/docs/ru/index.md +++ b/docs/ru/index.md @@ -18,7 +18,7 @@ | Создание проекта | Как начать новый проект? | | Генерация кода | Какие модули должны генерироваться из шаблонов? | | Добавление страницы | Как добавить новую страницу в проект? | -| Добавление UI-модуля | Как создать компонент, фичу, виджет, сущность или layout? | +| Добавление UI-модуля | Как создать компонент, бизнес-модуль, виджет или layout? | | Стилизация | Как стилизовать компоненты в проекте? | | Получение данных | Как получать данные с сервера? | | Управление состоянием | Как работать с состоянием? | @@ -31,7 +31,7 @@ | Раздел | Отвечает на вопрос | |--------|-------------------| | Технологии и библиотеки | Какой стек используем? | -| Архитектура | Как устроены слои FSD, зависимости, публичный API? | +| Архитектура | Как устроены слои SLM, зависимости, публичный API? | | Стиль кода | Как оформлять код: отступы, кавычки, импорты, early return? | | Именование | Как называть файлы, переменные, компоненты, хуки? | | Документирование | Как писать JSDoc: что документировать, а что нет? | @@ -44,7 +44,7 @@ | Раздел | Отвечает на вопрос | |--------|-------------------| | Настройка VS Code | Как настроить редактор для проекта? | -| Структура проекта | Как организованы папки и файлы по FSD? | +| Структура проекта | Как организованы папки и файлы по SLM? | | Компоненты | Как устроен компонент: файлы, пропсы, clsx? | | Page-level компоненты | Как описывать layout, page, loading, error, not-found? | | Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? | diff --git a/docs/ru/workflow.md b/docs/ru/workflow.md index ff67388..5d0c683 100644 --- a/docs/ru/workflow.md +++ b/docs/ru/workflow.md @@ -16,7 +16,7 @@ title: Workflow cd my-app npm install ``` -2. Проект готов к разработке — стек, структура FSD, конфигурация +2. Проект готов к разработке — стек, структура SLM, конфигурация редактора и шаблоны генерации уже настроены. ## Генерация кода @@ -27,13 +27,12 @@ title: Workflow | Модуль | Слой | Шаблон | |------------|--------------|-------------| - | Компонент | `shared/ui/` | `component` | - | Фича | `features/` | `feature` | - | Виджет | `widgets/` | `widget` | - | Сущность | `entities/` | `entity` | - | Layout | `layouts/` | `layout` | - | Экран | `screens/` | `screen` | - | Стор | `model/` | `store` | + | Компонент | `ui/` | `component` | + | Бизнес-модуль | `business/` | `module` | + | Виджет | `widgets/` | `widget` | + | Layout | `layouts/` | `layout` | + | Экран | `screens/` | `screen` | + | Стор | `stores/` | `store` | 2. Сгенерировать модуль из шаблона. 3. Если подходящего шаблона нет — сначала создать шаблон, затем использовать. @@ -53,7 +52,7 @@ title: Workflow ## Добавление UI-модуля -Создание компонента, фичи, виджета, сущности или layout. +Создание компонента, бизнес-модуля, виджета или layout. 1. Сгенерировать модуль из соответствующего шаблона в целевой слой. 2. Заполнить модуль логикой и стилями. diff --git a/docs/ru/workflow/code-generation.md b/docs/ru/workflow/code-generation.md index 0c67d74..2958653 100644 --- a/docs/ru/workflow/code-generation.md +++ b/docs/ru/workflow/code-generation.md @@ -10,13 +10,12 @@ title: Генерация кода | Модуль | Слой | Шаблон | |---|---|---| -| Компонент | `shared/ui/` | `component` | -| Фича | `features/` | `feature` | +| Компонент | `ui/` | `component` | +| Бизнес-модуль | `business/` | `module` | | Виджет | `widgets/` | `widget` | -| Сущность | `entities/` | `entity` | | Layout | `layouts/` | `layout` | | Экран | `screens/` | `screen` | -| Стор | `model/` | `store` | +| Стор | `stores/` | `store` | ## Что нужно знать diff --git a/docs/ru/workflow/creating-app.md b/docs/ru/workflow/creating-app.md index 5c35719..fbe7045 100644 --- a/docs/ru/workflow/creating-app.md +++ b/docs/ru/workflow/creating-app.md @@ -8,7 +8,7 @@ title: Создание проекта ## Что нужно знать -Новый проект создаётся из готового шаблона. Шаблон содержит настроенный стек, структуру FSD, конфигурацию редактора и шаблоны генерации кода — проект готов к разработке сразу после установки зависимостей. +Новый проект создаётся из готового шаблона. Шаблон содержит настроенный стек, структуру SLM, конфигурацию редактора и шаблоны генерации кода — проект готов к разработке сразу после установки зависимостей. ### Создание из шаблона @@ -24,7 +24,7 @@ npm install - Mantine UI + PostCSS Modules - Biome (линтинг и форматирование) - Zustand, SWR -- Структура FSD (`screens/`, `widgets/`, `features/`, `entities/`, `shared/`) +- Структура SLM (`layouts/`, `screens/`, `widgets/`, `business/`, `infrastructure/`, `ui/`, `shared/`) - Шаблоны генерации (`.templates/`) - Конфигурация VS Code (`.vscode/`) - CSS-токены (цвета, отступы, радиусы, медиа) diff --git a/docs/ru/workflow/creating-components.md b/docs/ru/workflow/creating-components.md index ff98f95..a59d2d6 100644 --- a/docs/ru/workflow/creating-components.md +++ b/docs/ru/workflow/creating-components.md @@ -4,7 +4,7 @@ title: Добавление UI-модуля # Добавление UI-модуля -Как создать компонент, фичу, виджет, сущность или layout в проекте. +Как создать компонент, бизнес-модуль, виджет или layout в проекте. ## Что нужно знать diff --git a/docs/ru/workflow/getting-started.md b/docs/ru/workflow/getting-started.md index 8e616f8..6b45648 100644 --- a/docs/ru/workflow/getting-started.md +++ b/docs/ru/workflow/getting-started.md @@ -8,13 +8,13 @@ title: Начало работы ## Стек проекта -**Next.js** (App Router), **Mantine**, **Zustand**, **FSD**. +**Next.js** (App Router), **Mantine**, **Zustand**, **SLM Design**. Подробнее — [Технологии и библиотеки](/basics/tech-stack). ## Ключевые особенности -- **Генерация вместо ручного создания** — компоненты, фичи, виджеты, сторы и другие модули не создаются вручную. Файловая структура генерируется из шаблонов `.templates/`. Ручное создание файловой структуры модулей запрещено. +- **Генерация вместо ручного создания** — компоненты, бизнес-модули, виджеты, сторы и другие модули не создаются вручную. Файловая структура генерируется из шаблонов `.templates/`. Ручное создание файловой структуры модулей запрещено. - **Biome вместо ESLint + Prettier** — один инструмент для линтинга и форматирования. Автофикс и сортировка импортов происходят автоматически при сохранении файла. ## Настройка окружения diff --git a/generated/ru/RULES.md b/generated/ru/RULES.md index 6b4456e..201e645 100644 --- a/generated/ru/RULES.md +++ b/generated/ru/RULES.md @@ -19,7 +19,7 @@ | Создание проекта | Как начать новый проект? | | Генерация кода | Какие модули должны генерироваться из шаблонов? | | Добавление страницы | Как добавить новую страницу в проект? | -| Добавление UI-модуля | Как создать компонент, фичу, виджет, сущность или layout? | +| Добавление UI-модуля | Как создать компонент, бизнес-модуль, виджет или layout? | | Стилизация | Как стилизовать компоненты в проекте? | | Получение данных | Как получать данные с сервера? | | Управление состоянием | Как работать с состоянием? | @@ -32,7 +32,7 @@ | Раздел | Отвечает на вопрос | |--------|-------------------| | Технологии и библиотеки | Какой стек используем? | -| Архитектура | Как устроены слои FSD, зависимости, публичный API? | +| Архитектура | Как устроены слои SLM, зависимости, публичный API? | | Стиль кода | Как оформлять код: отступы, кавычки, импорты, early return? | | Именование | Как называть файлы, переменные, компоненты, хуки? | | Документирование | Как писать JSDoc: что документировать, а что нет? | @@ -45,7 +45,7 @@ | Раздел | Отвечает на вопрос | |--------|-------------------| | Настройка VS Code | Как настроить редактор для проекта? | -| Структура проекта | Как организованы папки и файлы по FSD? | +| Структура проекта | Как организованы папки и файлы по SLM? | | Компоненты | Как устроен компонент: файлы, пропсы, clsx? | | Page-level компоненты | Как описывать layout, page, loading, error, not-found? | | Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? | @@ -74,7 +74,7 @@ cd my-app npm install ``` -2. Проект готов к разработке — стек, структура FSD, конфигурация +2. Проект готов к разработке — стек, структура SLM, конфигурация редактора и шаблоны генерации уже настроены. ### Генерация кода @@ -85,13 +85,12 @@ | Модуль | Слой | Шаблон | |------------|--------------|-------------| - | Компонент | `shared/ui/` | `component` | - | Фича | `features/` | `feature` | - | Виджет | `widgets/` | `widget` | - | Сущность | `entities/` | `entity` | - | Layout | `layouts/` | `layout` | - | Экран | `screens/` | `screen` | - | Стор | `model/` | `store` | + | Компонент | `ui/` | `component` | + | Бизнес-модуль | `business/` | `module` | + | Виджет | `widgets/` | `widget` | + | Layout | `layouts/` | `layout` | + | Экран | `screens/` | `screen` | + | Стор | `stores/` | `store` | 2. Сгенерировать модуль из шаблона. 3. Если подходящего шаблона нет — сначала создать шаблон, затем использовать. @@ -111,7 +110,7 @@ ### Добавление UI-модуля -Создание компонента, фичи, виджета, сущности или layout. +Создание компонента, бизнес-модуля, виджета или layout. 1. Сгенерировать модуль из соответствующего шаблона в целевой слой. 2. Заполнить модуль логикой и стилями. @@ -156,7 +155,7 @@ - `Next.js` — для продуктовых сайтов. #### Архитектура -- `FSD (Feature-Sliced Design)` — структура проекта и границы модулей. Используется кастомизированная версия — подробнее в разделе [Архитектура](/basics/architecture). +- `SLM Design` — собственная модульная архитектура проекта. Подробнее в разделе [Архитектура](/basics/architecture). #### UI компоненты - `Mantine UI` — базовые UI-компоненты. @@ -235,7 +234,7 @@ **Хорошо** ```text -features/ +business/ └── auth-by-email/ ├── ui/ │ └── login-form.tsx @@ -244,14 +243,14 @@ features/ ├── stores/ │ └── auth.store.ts ├── types/ - │ └── auth.interface.ts - ├── auth-by-email.feature.tsx + │ └── auth.type.ts + ├── auth-by-email.tsx └── index.ts ``` **Плохо** ```text -features/ +business/ └── authByEmail/ ├── LoginForm.tsx ├── useAuth.ts @@ -321,473 +320,660 @@ const users = {} as Record; ``` -## Архитектура +## SLM Design +Scoped Layered Module Design — модульная архитектура фронтенд-приложений. Код организован по слоям ответственности, а модуль содержит всё, что ему нужно: компоненты, хуки, сторы, типы, стили. -Раздел описывает архитектуру проекта: из каких слоёв состоит приложение, -как организован код внутри слоёв и какие правила управляют зависимостями. +### Преимущества -### Что нужно знать +#### Вертикальная организация домена -SLM Design (Scoped Layered Module Design) — архитектурный подход -к проектированию фронтенд-приложений, предложенный Громовым Сергеем в 2026 г. +Бизнес-домен не разбивается по техническим слоям — сценарии, сущности, типы и UI живут в одном модуле. Это сокращает время навигации и упрощает сопровождение: все изменения домена локализованы. -Вырос на основе: +#### Разделение ответственности без перегрузки слоёв -- [Feature-Sliced Design](https://feature-sliced.design) — слои и направление зависимостей -- Screaming Architecture — структура говорит сама за себя -- Colocation Principle — код рядом с местом использования +Сервисы приложения (`infrastructure/`), UI-кит (`ui/`) и общие ресурсы (`shared/`) — три разных слоя с разной природой. Ни один слой не превращается в свалку разнородного кода. -Переосмыслив эти подходы, SLM Design отличается от FSD в трёх аспектах: -где живёт код (колокация), как он организован (модули) -и как масштабируется (подъём при переиспользовании). +#### Горизонтальная инкапсуляция -### Терминология +Вложенные модули (`parts/`) и направление зависимостей позволяют нескольким разработчикам работать над одной областью приложения параллельно, не затрагивая код друг друга. -Архитектура оперирует четырьмя ключевыми понятиями: +#### Колокация по умолчанию -- **Слой** — содержит модули -- **Модуль** — содержит сегменты -- **Компонент** — содержит сегменты -- **Сегмент** — папка внутри модуля или компонента, группирующая код по назначению: UI-элементы (`ui/`), хуки (`hooks/`), типы (`types/`), стили (`styles/`) и другие +Код начинает жизнь рядом с местом использования и поднимается в общие слои только при реальной потребности. Глобальные слои не засоряются преждевременными абстракциями. -Модуль и компонент устроены одинаково — оба имеют сегменты. Разница в том, где они живут и обязателен ли UI. +#### Явное разделение каркаса и контента + +Каркас группы маршрутов (`layouts/`) и контент конкретной страницы (`screens/`) — независимые слои с собственной ответственностью. + +#### Масштабирование через группировку + +При росте проекта слои не теряют структуру — модули группируются по естественным признакам: бизнес-домены по субдоменам, страницы по разделам, UI-компоненты по уровню абстракции (примитивы и композиции). + +#### Dependency Injection без фреймворков + +Cross-domain зависимости в бизнес-слое реализуются через фабрики — модуль декларирует что ему нужно, а точка использования предоставляет зависимости. Домены изолированы без DI-контейнеров, провайдеров и шин событий. + +### Происхождение + +SLM Design вырос на основе: + +- **Feature-Sliced Design** — слоистая структура, публичный API модуля, направление зависимостей +- **Vertical Slice Architecture** — модуль как вертикальный срез, содержащий всё необходимое +- **Screaming Architecture** — структура проекта «кричит» о назначении: открыл `business/auth` — видишь авторизацию +- **Colocation Principle** — код живёт рядом с местом использования + +### Пример структуры проекта ```text -Слой -└── Модуль - ├── Сегменты (hooks/, stores/, types/, styles/, lib/...) - └── ui/ - └── Компонент - ├── Сегменты (hooks/, stores/, types/, styles/, lib/...) - └── ui/ - └── Компонент → ... +src/ +├── app/ +│ +├── layouts/ +│ ├── main/ +│ └── dashboard/ +│ +├── screens/ +│ ├── home/ +│ ├── products/ +│ ├── product-detail/ +│ └── about/ +│ +├── widgets/ +│ ├── page-heading/ +│ ├── hero-section/ +│ └── promo-banner/ +│ +├── business/ +│ ├── auth/ +│ ├── catalog/ +│ ├── orders/ +│ └── chat/ +│ +├── infrastructure/ +│ ├── theme/ +│ ├── i18n/ +│ ├── backend-api/ +│ └── logger/ +│ +├── ui/ +│ ├── button/ +│ ├── input/ +│ ├── modal/ +│ ├── toast/ +│ └── dropdown/ +│ +└── shared/ + ├── lib/ + ├── types/ + └── styles/ ``` -#### Слой +### Принципы -Архитектурный уровень. Содержит только модули. Определяет назначение кода и правила зависимостей. - -#### Модуль - -Единица первого уровня слоя, объединённая по смыслу. Может содержать компонент, логику, типы, стили — или любую комбинацию. Имеет публичный API (`index.ts`) и внутреннюю структуру из сегментов. - -Модуль — не обязательно UI. Feature `analytics` может быть только стором и сервисом. Entity `session` может быть только типами и хуком. - -Модуль не может содержать вложенных модулей. Вложенные единицы с UI размещаются в сегменте `ui/` как компоненты. - -#### Компонент - -Вложенная единица внутри сегмента `ui/` модуля (или другого компонента). Публичный `.tsx` файл обязателен. Именуется без суффикса слоя. - -Компонент может иметь собственные сегменты (`hooks/`, `styles/`, `types/` и т.д.), `index.ts` и свой `ui/` с ещё более вложенными компонентами. - -Отличия от модуля: - -| | Модуль | Компонент | -|--|--------|-----------| -| Где живёт | В корне слоя | В `ui/` модуля или другого компонента | -| Публичный `.tsx` | С суффиксом слоя, опционален | Без суффикса, обязателен | -| Может не иметь UI | Да | Нет | - -Пример: - -```text -features/auth-by-email/ # модуль -├── auth-by-email.feature.tsx # публичный .tsx модуля (с суффиксом, опционален) -├── ui/ # сегмент: компоненты -│ ├── login-form/ # компонент -│ │ ├── login-form.tsx # публичный .tsx компонента (без суффикса, обязателен) -│ │ ├── ui/ # вложенные компоненты -│ │ │ └── password-field/ -│ │ │ └── password-field.tsx -│ │ ├── hooks/ -│ │ │ └── use-validation.hook.ts -│ │ ├── styles/ -│ │ │ └── login-form.module.css -│ │ └── index.ts -│ └── reset-password/ # компонент -│ ├── reset-password.tsx -│ └── index.ts -├── hooks/ -│ └── use-auth.hook.ts -├── stores/ -│ └── auth.store.ts -└── index.ts -``` - -#### Сегмент - -Техническая папка внутри модуля или компонента, группирующая код по назначению. Набор не фиксирован — включаются только те сегменты, которые нужны. - -| Сегмент | Назначение | -|---------|-----------| -| `ui/` | Вложенные компоненты | -| `hooks/` | React-хуки | -| `stores/` | Сторы состояния | -| `types/` | Интерфейсы, типы, enums, DTO | -| `styles/` | Стили | -| `lib/` | Утилиты | -| `services/` | Внешние источники данных | -| `helpers/` | Вспомогательные функции | -| `config/` | Константы, конфигурация | - -### Ключевой принцип - -> Модуль живёт на самом низком уровне, где он используется. -> Поднимается выше только при переиспользовании в 2+ местах. - -Если модуль используется только в одном месте — он остаётся на текущем уровне. -Как только он начинает использоваться в 2+ местах — выносится на уровень выше. -В крайнем случае — в `shared/`, где он доступен всем. +- **Домен — единое целое.** Всё, что относится к домену, живёт в одном модуле. +- **Колокация.** Код рождается рядом с местом использования и поднимается только при необходимости. +- **Зависимости однонаправлены.** Импорты только сверху вниз, только через публичный API. +- **Архитектура — каркас, не клетка.** Правила фиксируют направление зависимостей и структуру модуля, остальное определяет команда. ### Слои -Каждый нижний слой не знает о существовании верхних. Импорты идут строго сверху вниз. +Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом. + +#### Определение + +**Слой — уровень организации кода внутри `src/`. Каждый слой отвечает за свою область (каркас страницы, бизнес-логика, UI-кит) и задаёт правила для кода внутри: направление импортов, именование, допустимые связи между модулями.** + +#### Группы слоёв + +Слои делятся на три группы: + +| Группа | Слои | Описание | +|--------|------|----------| +| Композиция | `app`, `layouts`, `screens`, `widgets` | Собирают интерфейс из готовых блоков: маршруты, каркасы, страницы | +| Ядро | `business`, `infrastructure`, `ui` | Реализация продукта: бизнес-домены, техсервисы, UI-кит | +| Фундамент | `shared` | Общие ресурсы: утилиты, хелперы, стили, конфиги | + +#### Направление зависимостей + +Любой импорт между модулями — только через публичный API. ``` -app → layouts → screens → widgets → features → entities → shared +app → [ layouts | screens ] → widgets → business → infrastructure → ui → shared ``` -| Слой | Что лежит | Импортирует | -|------|-----------|-------------| -| **App** | Роутинг, провайдеры, глобальные стили. Композиция layout + screen для маршрута. | Все слои ниже | -| **Layouts** | Каркас страницы, общий для группы маршрутов. | widgets, features, entities, shared | -| **Screens** | Контент конкретной страницы. | widgets, features, entities, shared | -| **Widgets** | Составные блоки с данными/логикой, переиспользуемые в 2+ местах. | features, entities, shared | -| **Features** | Пользовательское действие или интерактивный сценарий. | entities, shared | -| **Entities** | Бизнес-сущность с отображением и типами. | shared | -| **Shared** | Переиспользуемые компоненты, утилиты, стили без бизнес-логики. | ничего | +- `layouts` и `screens` — параллельные слои, не импортируют друг друга +- Модули одного слоя в группе «Композиция» изолированы друг от друга +- Модули одного слоя `infrastructure` и `ui` могут импортировать друг друга через публичный API +- Модули `business` — cross-domain зависимости по коду через фабрику, `import type` напрямую +- Импорт типов (`import type`) в «Ядре» разрешён в обоих направлениях -Принципы: -- Импорты строго сверху вниз -- Модули одного слоя не знают друг о друге -- Layout получает контекстно-зависимые блоки через пропсы от app, а не импортирует их сам -- `entities/` и `features/` создаются осознанно — это не результат «вынесения» компонента из screen +#### Слой App -#### App +Точка входа приложения. Отвечает за запуск, роутинг и композицию маршрутов из layout и screen. -Точка входа приложения: роутинг (Next.js App Router), провайдеры, глобальные стили. -Находится на самом высоком уровне абстракции — может импортировать любой слой ниже. -Никакой бизнес-логики — только композиция. +В отличие от остальных слоёв, `app/` не содержит модулей SLM. Здесь живут только инфраструктурные файлы, которые не могут быть никаким другим слоем: файлы фреймворка роутинга, точка запуска и код инициализации. -```text -app/ -├── layout.tsx # RootLayout: провайдеры, глобальные стили -├── page.tsx # Главная: MainLayout + HomeScreen -├── knv-new/ -│ └── page.tsx # КНВ: MainLayout + KnvScreen -└── catalog/ - └── page.tsx # Каталог: MainLayout + CatalogScreen -``` +##### Требования -```tsx -// app/knv-new/page.tsx -import { MainLayout } from '@/layouts/main' -import { KnvScreen } from '@/screens/knv' +- Не содержит модулей SLM — только файлы фреймворка, роутинг, инициализация +- Содержит: файлы маршрутов, bootstrap, обработку ошибок верхнего уровня (404, error boundary), подключение глобальных стилей и ассетов +- Провайдеры и гарды — только подключает готовые из нижних слоёв, не реализует +- Не содержит бизнес-логику, UI-компоненты, хуки, сторы, сервисы +- Никем не импортируется -export default function KnvNewPage() { - return ( - - - - ) -} -``` +#### Слой Layouts -Если layout требует разный контент в зависимости от страницы — app передаёт его через пропсы: - -```tsx -// app/knv-new/page.tsx -import { MainLayout } from '@/layouts/main' -import { KnvScreen } from '@/screens/knv' -import { KnvHeader } from '@/widgets/knv-header' - -export default function KnvNewPage() { - return ( - }> - - - ) -} -``` - -#### Layouts - -Каркас страницы — общие элементы, одинаковые для группы маршрутов (header, footer, sidebar). - -Если компонент внутри layout начинает использоваться в 2+ местах — он выносится в `widgets/` или `shared/ui/`. +Каркас страницы: общие элементы, одинаковые для группы маршрутов (header, footer, sidebar). ```text src/layouts/ -└── main/ - ├── main.layout.tsx - ├── ui/ - │ ├── header/ - │ │ └── header.tsx - │ └── footer/ - │ └── footer.tsx - └── index.ts +├── main/ +├── dashboard/ +└── auth/ ``` -#### Screens +##### Требования -Контент конкретной страницы. Собирает локальные секции и переиспользуемые модули из нижних слоёв. +- Содержит только модули +- Не содержит бизнес-логику +- Контекстно-зависимые блоки принимает через пропсы от `app`, не импортирует напрямую -Если компонент внутри screen начинает использоваться в 2+ местах — он выносится в `widgets/` или `shared/ui/`. +#### Слой Screens + +Контент конкретной страницы: собирает её из модулей нижних слоёв. ```text src/screens/ -└── knv/ - ├── knv.screen.tsx - ├── ui/ - │ ├── hero-section/ - │ │ └── hero-section.tsx - │ ├── products-section/ - │ │ └── products-section.tsx - │ └── diseases-section/ - │ └── diseases-section.tsx - └── index.ts +├── home/ +├── products/ +├── product-detail/ +├── about/ +└── contacts/ ``` -#### Widgets +Когда количество страниц затрудняет навигацию — вводится группировка по разделам. Группа — папка для организации, не модуль (без `index.ts`). -Составные блоки с данными и логикой, переиспользуемые в 2+ местах. +```text +src/screens/ +├── shop/ +│ ├── home/ +│ ├── products/ +│ ├── product-detail/ +│ └── cart/ +├── account/ +│ ├── profile/ +│ ├── settings/ +│ └── order-history/ +└── info/ + ├── about/ + ├── contacts/ + └── faq/ +``` -Если блок с логикой нужен только в одном месте — это компонент внутри `screens/{name}/ui/` или `layouts/{name}/ui/`, а не widget. +##### Требования + +- Содержит только модули +- Не содержит бизнес-логику +- Локальные одноразовые секции живут внутри screen-модуля, не выносятся в `widgets`/`business` + +#### Слой Widgets + +Составной блок интерфейса, который компонует модули ядра, но не принадлежит конкретному бизнес-домену. Widget появляется когда блок используется в нескольких screens или layouts. + +Если блок принадлежит домену — он живёт в `business/{area}/`, даже если переиспользуется. Если блок нужен только в одном месте — это `screens/{name}/parts/` или `layouts/{name}/parts/`, а не widget. ```text src/widgets/ -└── popular-products-slider/ - ├── popular-products-slider.widget.tsx - ├── ui/ - │ └── slider-card/ - │ └── slider-card.tsx - ├── hooks/ - │ └── use-products.hook.ts - └── index.ts +├── page-heading/ +├── hero-section/ +├── onboarding-checklist/ +├── promo-banner/ +└── error-boundary/ ``` -#### Features +##### Требования -Пользовательское действие или интерактивный сценарий: авторизация, заказ, добавление в корзину. +- Не принадлежит конкретному бизнес-домену. Если блок доменный — он живёт в `business/` +- Используется в нескольких screens или layouts -Feature создаётся осознанно, когда есть действие пользователя с бизнес-логикой. Компонент опционален — feature может экспортировать хуки, сторы, компоненты или всё вместе. +#### Слой Business + +Бизнес-домены приложения: auth, catalog, orders, checkout, chat. Каждый домен — отдельный модуль со своими типами, логикой, UI и сервисами. + +Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`. Cross-domain зависимости по коду реализуются через фабрику. `import type` между доменами разрешён напрямую. + +Business объединяет то, что в FSD разделено на `features` и `entities`: пользовательские сценарии и бизнес-сущности живут вместе, внутри одного домена. Внутри домена сегменты разделяют ответственность: `types/` — доменная модель, `hooks/` и `services/` — сценарии и логика, `mappers/` — трансформация данных, `parts/` — составные блоки. ```text -src/features/ -└── auth-by-email/ - ├── auth-by-email.feature.tsx - ├── ui/ - │ ├── login-form/ - │ │ └── login-form.tsx - │ └── reset-password/ - │ └── reset-password.tsx - ├── hooks/ - │ └── use-auth.hook.ts - ├── stores/ - │ └── auth.store.ts - └── index.ts +src/business/ +├── auth/ +├── catalog/ +├── orders/ +├── checkout/ +└── chat/ ``` -#### Entities - -Бизнес-сущность с отображением и типами: препарат, заболевание, врач, пользователь. - -Entity создаётся осознанно, когда появляется бизнес-сущность. Компонент опционален — entity может быть только типами и хуком. - -Отличие от `shared/ui/`: entity-компонент знает о бизнес-домене (принимает `Product`, а не абстрактные пропсы). +Когда количество доменов затрудняет навигацию — вводится группировка по субдоменам. Группа — папка для организации, не модуль (без `index.ts`). ```text -src/entities/ -├── product/ -│ ├── product.entity.tsx -│ ├── ui/ -│ │ └── product-card/ -│ │ └── product-card.tsx -│ ├── types/ -│ │ └── product.type.ts -│ └── index.ts -│ -├── session/ -│ ├── types/ -│ │ └── session.type.ts -│ ├── hooks/ -│ │ └── use-session.hook.ts -│ └── index.ts +src/business/ +├── commerce/ +│ ├── catalog/ +│ ├── cart/ +│ ├── orders/ +│ └── checkout/ +└── communication/ + ├── chat/ + └── notifications/ ``` -#### Shared +##### Требования -Переиспользуемые компоненты, утилиты, стили без бизнес-логики. Не знает о бизнес-домене — работает с абстрактными данными. +- Один модуль = один бизнес-домен +- Циклические зависимости между доменами запрещены +- Импорт кода между доменами — через фабрику. `import type` — напрямую +- Доменные типы (`User`, `Product`) живут здесь, не в `shared/` -Структурирован как набор сегментов: +#### Слой Infrastructure + +Техсервисы приложения: theme, i18n, API-адаптеры, logger, realtime. Каждый сервис — отдельный модуль. + +Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`. + +Отличие от `shared/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги). + +```text +src/infrastructure/ +├── theme/ +├── i18n/ +├── backend-api/ +├── maps-api/ +├── logger/ +├── feature-flags/ +└── realtime/ +``` + +##### Требования + +- Один модуль = один техсервис +- Импортирует `infrastructure/`, `ui/`, `shared/` + +#### Слой UI + +UI-кит без бизнес-логики: button, carousel, toast, modal. + +Слой входит в группу «Ядро». Импортирует `ui/` и `shared/`. + +Компоненты строятся друг на друге: `button` использует `icon`, `carousel` использует `button`. + +```text +src/ui/ +├── button/ +├── input/ +├── icon/ +├── carousel/ +├── modal/ +├── toast/ +├── dropdown/ +├── tabs/ +└── tooltip/ +``` + +Когда количество компонентов затрудняет навигацию — вводится группировка на примитивы и композиции. Примитивы (`button`, `icon`, `input`) не импортируют композиции. Композиции (`carousel`, `modal`, `dropdown`) строятся на примитивах. + +```text +src/ui/ +├── primitives/ +│ ├── button/ +│ ├── input/ +│ ├── icon/ +│ └── badge/ +└── composites/ + ├── carousel/ + ├── modal/ + ├── dropdown/ + ├── tabs/ + └── tooltip/ +``` + +##### Требования + +- Не содержит бизнес-логику +- Импортирует только `ui/` и `shared/` + +#### Слой Shared + +Общие ресурсы: утилиты, хелперы, стили, конфиги. Не знает о бизнес-домене. + +Слой входит в группу «Фундамент» — ни о ком не знает, никого не импортирует. + +Отличие от `infrastructure/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги). + +Отличие от `ui/`: UI-компоненты (button, carousel, modal) живут в слое `ui/`, а не здесь. ```text src/shared/ -├── ui/ -│ ├── icon/ -│ │ └── icon.tsx -│ ├── carousel/ -│ │ ├── carousel.tsx -│ │ ├── ui/ -│ │ │ ├── carousel-slide/ -│ │ │ │ └── carousel-slide.tsx -│ │ │ └── carousel-dots/ -│ │ │ └── carousel-dots.tsx -│ │ └── index.ts -│ ├── container/ -│ └── button/ ├── lib/ -│ ├── format-date.ts -│ └── cn.ts +├── types/ ├── styles/ -│ ├── variables.css -│ └── media.css └── sprites/ ``` -### Модуль +##### Требования + +- Не имеет runtime-состояния + +### Модули + +Раздел описывает модули SLM: что такое модуль, из чего он состоит и как взаимодействует с остальным кодом. + +#### Определение + +**Модуль — универсальный строительный блок архитектуры. Живёт на слое и содержит всё необходимое для своей работы: компоненты, хуки, сторы, сервисы, типы, стили. Набор содержимого не фиксирован — включаются только нужные части.** + +#### Модуль vs компонент + +**Компонент** — один `.tsx` файл. Не имеет своих сегментов, использует сегменты родительского модуля. Живёт в корне или `ui/` сегменте модуля. + +**Модуль** — папка, которая может содержать корневой компонент, сегменты (`hooks/`, `types/`, `styles/`, `ui/`, `parts/` и т.д.) и публичный API (`index.ts`). + +```text +auth/ +├── ui/ +│ ├── auth-guard.tsx +│ └── logout-button.tsx +├── parts/ +│ ├── login-form/ +│ ├── registration-form/ +│ └── restore-form/ +├── hooks/ +├── stores/ +├── types/ +├── auth.tsx # корневой компонент (опционален) +└── index.ts +``` #### Структура -```text -{name}/ -├── {name}.{суффикс}.tsx # компонент (опционален) -├── ui/ # вложенные компоненты -├── hooks/ # хуки -├── stores/ # сторы -├── types/ # типы, интерфейсы, enums -├── styles/ # стили -├── lib/ # утилиты -├── services/ # внешние источники данных -├── helpers/ # вспомогательные функции -├── config/ # константы, конфигурация -└── index.ts # публичный API -``` - -#### Именование компонента - -Суффикс слоя получают **только модули первого уровня слоя** — те, что лежат непосредственно в корне слоя. Все компоненты (в `ui/`, любой глубины) именуются без суффикса. Без исключений. - -| Слой | Суффикс | Пример | -|------|---------|--------| -| Layouts | `.layout.tsx` | `main.layout.tsx` | -| Screens | `.screen.tsx` | `knv.screen.tsx` | -| Widgets | `.widget.tsx` | `popular-products-slider.widget.tsx` | -| Features | `.feature.tsx` | `auth-by-email.feature.tsx` | -| Entities | `.entity.tsx` | `product.entity.tsx` | - -Примеры: +Модуль состоит из сегментов. Ни один сегмент не обязателен — модуль может состоять даже из одного `index.ts` с реэкспортом типов. ```text -features/auth-by-email/auth-by-email.feature.tsx # модуль первого уровня → суффикс -features/auth-by-email/ui/login-form/login-form.tsx # компонент в ui/ → без суффикса -shared/ui/carousel/carousel.tsx # компонент в shared → без суффикса +{module-name}/ +├── {module-name}.tsx # корневой компонент (опционален) +├── ui/ # компоненты модуля (только .tsx) +├── parts/ # вложенные модули (со своими сегментами) +├── hooks/ # хуки +├── stores/ # сторы состояния +├── services/ # внешние источники данных +├── mappers/ # трансформация данных между форматами +├── types/ # типы +├── styles/ # стили +├── lib/ # утилиты модуля +├── config/ # константы +└── index.ts # публичный API ``` -#### Правила импорта +Подробное описание каждого сегмента — в разделе [Сегменты](/reference/segments). -Три уровня правил: +#### Публичный API -**Между слоями** — импорты строго сверху вниз: +Модуль экспортирует наружу только то, что нужно другим. Всё остальное — внутреннее. -``` -app → layouts → screens → widgets → features → entities → shared +```ts +// business/auth/index.ts +export type { User, Session } from './types/user.types' +export { useAuth } from './hooks/use-auth.hook' +export { AuthGuard } from './ui/auth-guard' ``` -**Внутри модуля (не shared)** — сегменты доступны друг другу и компонентам. Компоненты внутри одного `ui/` не импортируют друг друга: +Импорт в обход `index.ts` запрещён: + +```ts +// Плохо +import { validateToken } from '@/business/auth/lib/tokens' + +// Хорошо +import { useAuth } from '@/business/auth' +``` + +#### Фабрика + +Если модуль зависит от кода другого бизнес-домена — он экспортирует фабрику. Фабрика декларирует необходимые зависимости и возвращает API модуля. Точка использования (screen, widget, layout) предоставляет зависимости при вызове. + +Модуль без cross-domain зависимостей экспортирует API напрямую. Типы всегда экспортируются напрямую — `import type` не является runtime-зависимостью. + +##### Модуль без зависимостей — прямой экспорт: + +```ts +// business/auth/index.ts +export { useAuth } from './hooks/use-auth' +export { useCurrentUser } from './hooks/use-current-user' +export type { User, Session } from './types' +``` + +##### Модуль с зависимостями — фабрика: + +```ts +// business/chat/types/deps.ts +import type { User } from '@/business/auth' + +export interface ChatDeps { + useCurrentUser: () => User | null +} +``` + +```ts +// business/chat/index.ts +import type { ChatDeps } from './types/deps' + +export function chatFactory(deps: ChatDeps) { + return { + useMessages: (roomId: string) => { + const user = deps.useCurrentUser() + // ... + }, + useSendMessage: (roomId: string) => { + const user = deps.useCurrentUser() + return (text: string) => { /* ... */ } + }, + useChatRooms: () => { + const user = deps.useCurrentUser() + // ... + }, + ChatBadge: ({ count }: { count: number }) => { /* ... */ }, + } +} + +export type { Message, ChatRoom } from './types' +export type { ChatDeps } from './types/deps' +``` + +##### Использование на странице: + +```tsx +// screens/support/support.tsx +import { useCurrentUser } from '@/business/auth' +import { chatFactory } from '@/business/chat' + +const chat = chatFactory({ useCurrentUser }) + +export function SupportScreen() { + const { useMessages, useSendMessage, ChatBadge } = chat + const messages = useMessages('support') + const sendMessage = useSendMessage('support') + + return ( +
+ + {messages.map(m => )} + +
+ ) +} +``` + +#### Жизненный цикл + +Модуль рождается на самом низком уровне использования и поднимается выше только при реальной потребности. + +- Нужен на одной странице → `screens/{name}/parts/` +- Появился в 2+ местах → поднимается по природе: + - абстрактный UI → `ui/` + - блок с данными/логикой → `widgets/` + - представление бизнес-домена → `business/{area}/parts/` + +Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность. + +### Сегменты + +Раздел описывает сегменты SLM: что такое сегмент, какие бывают и что в каждом из них лежит. + +#### Определение + +**Сегмент — папка внутри модуля, которая группирует файлы по назначению. Набор сегментов не фиксирован — модуль включает только те, которые ему нужны. Команда сама определяет какие сегменты используются в проекте — архитектура даёт рекомендацию.** + +#### Обзор + +| Сегмент | Содержимое | +|---------|------------| +| `ui/` | Компоненты модуля — только `.tsx` файлы | +| `parts/` | Вложенные модули со своими сегментами | +| `hooks/` | React-хуки | +| `stores/` | Сторы состояния | +| `services/` | Работа с внешними источниками данных | +| `mappers/` | Трансформация данных между форматами | +| `types/` | TypeScript-типы и интерфейсы | +| `styles/` | Стили | +| `lib/` | Утилиты и хелперы модуля | +| `config/` | Константы и конфигурация | + +#### Сегмент ui/ + +Компоненты, принадлежащие модулю. Содержит только `.tsx` файлы — без своих сегментов, стилей, типов, хуков. Использует сегменты родительского модуля. ```text -features/auth-by-email/ +auth/ ├── ui/ -│ ├── login-form/ # НЕ может импортировать reset-password -│ └── reset-password/ # НЕ может импортировать login-form +│ ├── auth-provider.tsx +│ ├── auth-guard.tsx +│ └── logout-button.tsx +├── types/ +├── hooks/ +└── index.ts ``` -Если двум компонентам нужен общий код — он поднимается на уровень выше: +Если компоненту нужны собственные сегменты — это уже не `ui/`, а `parts/`. + +#### Сегмент parts/ + +Вложенные модули со своими сегментами. Каждый элемент `parts/` — полноценный модуль: папка с компонентом, хуками, стилями, типами и т.д. ```text -features/auth/ui/login-form/ui/email-input/ # нужен соседу -→ features/auth/ui/email-input/ # поднимаем на уровень -→ shared/ui/email-input/ # если нужен за пределами фичи +home/ +├── parts/ +│ ├── hero-section/ +│ │ ├── hero-section.tsx +│ │ ├── styles/ +│ │ └── parts/ +│ │ └── top-banner/ +│ │ └── top-banner.tsx +│ └── features-section/ +│ ├── features-section.tsx +│ └── hooks/ +├── home.screen.tsx +└── index.ts ``` -Компоненты наследуют правила зависимостей **родительского слоя**: +Отличие от `ui/`: элемент `parts/` — модуль со своими сегментами. Элемент `ui/` — компонент, один `.tsx` файл. -- Компонент внутри `features/auth/ui/login-form/` может импортировать `entities/` и `shared/` — как и сам feature -- Компонент внутри `widgets/hero/ui/hero-stats/` может импортировать `features/`, `entities/`, `shared/` — как и сам widget +Вложенность `parts/` инкапсулирует область разработки горизонтально: каждый разработчик работает в своём `parts/`-модуле, не затрагивая чужие. Это снижает конфликты при параллельной разработке. -**Shared** — без ограничений на внутренние импорты. Компоненты в `shared/ui/` могут импортировать друг друга (`button` использует `icon`), `ui/` может использовать `lib/` и другие сегменты. Shared — фундамент, его компоненты строятся друг на друге. +Если вложенный модуль обрастает своими `parts/` — это сигнал, что он достаточно самостоятельный для подъёма на уровень выше. -Правила импорта между слоями enforceable через ESLint — настройка границ слоёв и запрет обратных зависимостей. +#### Сегмент hooks/ -### Жизненный цикл модуля +React-хуки модуля. Инкапсулируют логику, состояние, подписки, побочные эффекты. -Модуль не проектируется «на вырост». Он рождается на самом низком уровне -и поднимается только когда появляется реальная потребность. +```text +hooks/ +├── use-auth.hook.ts +├── use-session.hook.ts +└── use-permissions.hook.ts +``` -Пример пути компонента `product-card`: +#### Сегмент stores/ -1. **Начало:** `screens/catalog/ui/product-card/` — нужен только на странице каталога. -2. **Переиспользование:** появился на странице поиска — выносим выше. - Куда именно зависит от природы: - - Составной блок с данными и логикой → `widgets/product-card/` - - Представление бизнес-сущности → `entities/product/ui/product-card/` - - Абстрактный UI без бизнес-логики → `shared/ui/product-card/` +Сторы состояния модуля. Конкретная реализация зависит от выбранного стейт-менеджера (Zustand, MobX, Redux и т.д.). -Основной триггер подъёма — переиспользование в 2+ местах. -Но если очевидно что модуль будет переиспользоваться — разумно разместить его на нужном уровне сразу. +```text +stores/ +├── auth.store.ts +└── session.store.ts +``` -Как происходит подъём на практике: +#### Сегмент services/ -1. Разработчик обнаруживает что компонент нужен в другом месте -2. Определяет целевой слой по природе компонента (widget / entity / shared) -3. Перемещает папку, обновляет импорты, добавляет суффикс если это модуль первого уровня слоя -4. Ревью подтверждает что подъём обоснован +Работа с внешними источниками данных: API-вызовы, запросы, подписки. -Подъём — это обычный рефакторинг в рамках задачи, а не отдельная активность. +```text +services/ +├── auth.service.ts +└── token.service.ts +``` -### Граничные случаи +#### Сегмент mappers/ -| Ситуация | Решение | Почему | -|----------|---------|--------| -| Фильтр каталога только на одной странице, но с хуками и стором | Модуль в `screens/catalog/` с сегментами `hooks/`, `stores/` | Не feature — feature это действие пользователя с бизнес-логикой (авторизация, заказ), а не UI с состоянием | -| Компонент используется в 2 местах на одной странице | Остаётся в `screens/{name}/ui/` | Переиспользование внутри одного screen — не повод выносить в widget | -| Entity без UI (только типы и хук) | Нормально | Модуль не обязан иметь UI. `entities/session/` с типами и хуком — валидный модуль | -| Компонент нужен и в layout, и в screen | Выносить в `widgets/` или `shared/ui/` | Два разных слоя используют один компонент → переиспользование → подъём | -| Модуль screen содержит бизнес-логику (хуки, стор, сервисы) | Нормально | Это не значит что он должен стать feature. Модуль с логикой внутри screen — обычное дело, пока он не переиспользуется | -| Компонент в `shared/ui/button` хочет использовать `shared/ui/icon` | Разрешено | Shared — исключение: внутренние импорты без ограничений. Это фундамент, его компоненты строятся друг на друге | +Функции трансформации данных из одного формата в другой: DTO в доменный тип, доменный тип в DTO, доменный тип в ViewModel. -### Запрещено +```text +mappers/ +├── map-user.ts +├── map-product.ts +└── map-order-to-dto.ts +``` -- **Не создавать feature без бизнес-логики** — кнопка без состояния и сайд-эффектов это компонент в `ui/`, а не feature -- **Не класть доменные типы в shared** — если тип знает о Product, Disease, User — он живёт в `entities/`, не в `shared/` -- **Не создавать entity или feature как результат «вынесения»** — они создаются осознанно: появилась бизнес-сущность → entity, появилось действие пользователя → feature -- **Не импортировать соседние компоненты в `ui/` (кроме shared)** — если двум компонентам нужен общий код, он поднимается на уровень выше -- **Не хранить в shared «помойку для всего»** — shared содержит переиспользуемые компоненты и утилиты без бизнес-логики, а не код который «непонятно куда положить» +#### Сегмент types/ -### Почему так, а не иначе +TypeScript-типы и интерфейсы модуля. Доменные типы, DTO, пропсы компонентов. -#### Почему `ui/` а не `modules/` +```text +types/ +├── user.type.ts +└── session.type.ts +``` -Внутри сегмента `ui/` всегда лежат единицы с обязательным UI (компоненты). Название точно отражает содержимое. `modules/` был нейтральнее, но скрывал природу вложенных единиц и создавал путаницу — модуль внутри модуля размывал понятие «модуль как единица слоя». +#### Сегмент styles/ -#### Почему модуль и компонент — разные понятия +Стили модуля. Формат зависит от выбранного подхода (CSS Modules, SCSS, CSS-in-JS и т.д.). -Модуль — единица первого уровня слоя, может не иметь UI. Компонент — вложенная единица с обязательным UI. Разделение снимает вопрос «а если нет `.tsx` — это всё ещё компонент?» и делает название сегмента `ui/` честным. +```text +styles/ +├── auth.module.css +└── login-form.module.css +``` -#### Почему shared без ограничений на внутренние импорты +#### Сегмент lib/ -Shared — фундамент. Его компоненты строятся друг на друге: `button` использует `icon`, `carousel` использует `button`. Запрещать это — бороться с реальностью. Поднимать некуда — shared уже нижний слой. +Утилиты и хелперы, специфичные для модуля. Чистые функции без побочных эффектов. -#### Почему нет слоя pages +```text +lib/ +├── validate-email.ts +└── format-phone.ts +``` -Роутинг живёт в `app/` (Next.js App Router). Отдельный слой `pages` конфликтовал бы с файловой структурой Next.js и дублировал ответственность `app/`. +Отличие от `shared/lib/`: здесь лежат утилиты, нужные только этому модулю. Общие утилиты — в `shared/lib/`. -#### Почему компоненты в одном `ui/` не импортируют друг друга (кроме shared) +#### Сегмент config/ -Независимые компоненты легко выносить в другой слой при переиспользовании. Если компонент A зависит от соседа B — при подъёме A придётся тянуть B. Это усложняет рефакторинг и нарушает принцип колокации. +Константы и конфигурация модуля: маршруты, лимиты, дефолтные значения. + +```text +config/ +├── routes.ts +└── constants.ts +``` ## Стиль кода @@ -1165,19 +1351,20 @@ public/ ```text src/ ├── app/ # Роутинг Next.js, провайдеры, глобальные стили -├── screens/ # Собраные страницы (UI) -├── layouts/ # Шаблоны -├── widgets/ # Крупные самостоятельные блоки интерфейса -├── features/ # Пользовательские сценарии -├── entities/ # Бизнес-сущности -└── shared/ # Переиспользуемый код (UI, утилиты, типы и др.) +├── layouts/ # Каркасы страниц (header, footer, sidebar) +├── screens/ # Контент конкретной страницы +├── widgets/ # Составные блоки интерфейса, не привязанные к домену +├── business/ # Бизнес-домены (auth, catalog, orders) +├── infrastructure/ # Техсервисы (theme, i18n, API-адаптеры) +├── ui/ # UI-кит без бизнес-логики +└── shared/ # Общие ресурсы (утилиты, типы, стили) ``` Принципы организации слоёв описаны в разделе [Архитектура](../basics/architecture). #### Папка `app/` -Совмещает два слоя: инициализацию приложения по FSD (провайдеры, глобальные стили) и файловый роутинг Next.js (`layout.tsx`, `page.tsx`, route-сегменты). +Точка входа приложения: инициализация (провайдеры, глобальные стили) и файловый роутинг Next.js (`layout.tsx`, `page.tsx`, route-сегменты). ```text src/app/ @@ -1197,8 +1384,7 @@ src/app/ ├── screen/ # Шаблон экрана ├── layout/ # Шаблон layout ├── widget/ # Шаблон виджета -├── feature/ # Шаблон фичи -├── entity/ # Шаблон сущности +├── module/ # Шаблон бизнес-модуля └── store/ # Шаблон стора ``` @@ -1537,11 +1723,10 @@ npx @gromlab/create <шаблон> <имя> <путь> | Команда | Что создаёт | |---|---| | `npx @gromlab/create component button src/shared/ui` | Компонент | -| `npx @gromlab/create feature auth src/features` | Фичу | +| `npx @gromlab/create module auth src/business` | Бизнес-модуль | | `npx @gromlab/create widget header src/widgets` | Виджет | -| `npx @gromlab/create entity user src/entities` | Сущность | | `npx @gromlab/create layout admin src/layouts` | Layout | -| `npx @gromlab/create store auth src/shared/model` | Стор | +| `npx @gromlab/create store auth src/business/auth/stores` | Стор | :::