docs(architecture): переработать раздел архитектуры
- заменил описание слоёв и модулей FSD на новую модель с компонентами - добавил раздел 'Что важно знать' с пояснением что архитектура надстройка над FSD - добавил описание master component и его правил - добавил раздел сегментов с таблицей - убрал дублирующиеся правила зависимостей и публичного API
This commit is contained in:
@@ -64,14 +64,6 @@
|
||||
|
||||
Порядок действий при разработке — от создания проекта до реализации фич.
|
||||
|
||||
### Начало работы
|
||||
|
||||
Подготовка окружения перед началом разработки.
|
||||
|
||||
1. Открыть проект в VS Code.
|
||||
2. Установить рекомендуемые расширения (редактор предложит автоматически).
|
||||
3. Ознакомиться со стеком: Next.js (App Router), Mantine, Zustand, FSD.
|
||||
|
||||
### Создание проекта
|
||||
|
||||
Инициализация нового проекта из готового шаблона.
|
||||
@@ -155,99 +147,245 @@
|
||||
<!-- /basics/tech-stack -->
|
||||
## Технологии и библиотеки
|
||||
|
||||
Базовый стек технологий и библиотек, на который опираются проекты и примеры в документации.
|
||||
Этот раздел описывает базовый стек технологий и библиотек, принятый в проекте.
|
||||
|
||||
### Что используем
|
||||
|
||||
#### Стек
|
||||
- **React/TypeScript** — основной стек для UI и приложения.
|
||||
- **Next.js** — для продуктовых сайтов.
|
||||
- `React` / `TypeScript` — основной стек для UI и приложения.
|
||||
- `Next.js` — для продуктовых сайтов.
|
||||
|
||||
#### Архитектура
|
||||
- **FSD (Feature-Sliced Design)** — структура проекта и границы модулей.
|
||||
- `FSD (Feature-Sliced Design)` — структура проекта и границы модулей. Используется кастомизированная версия — подробнее в разделе [Архитектура](/basics/architecture).
|
||||
|
||||
#### UI компоненты
|
||||
- **Mantine UI** — базовые UI-компоненты.
|
||||
- `Mantine UI` — базовые UI-компоненты.
|
||||
|
||||
#### Fetch (API)
|
||||
- **@gromlab/api-codegen** — генерация API‑клиентов и типов.
|
||||
- **SWR** — получение, кеширование, ревалидация, дедубликация.
|
||||
- **SWR (useSWRSubscription)** - сокеты, реалтайм подписки.
|
||||
#### Работа с данными (API)
|
||||
- `@gromlab/api-codegen` — генерация API‑клиентов и типов.
|
||||
- `SWR` — получение, кеширование, ревалидация, дедубликация.
|
||||
- `SWR (useSWRSubscription)` — сокеты, реалтайм подписки.
|
||||
|
||||
#### Store
|
||||
- **Zustand** — глобальное состояние.
|
||||
- `Zustand` — глобальное состояние.
|
||||
|
||||
#### Локализация
|
||||
- **i18next (i18n)** — локализация всех пользовательских текстов.
|
||||
- `i18next (i18n)` — локализация всех пользовательских текстов.
|
||||
|
||||
#### Тестирование
|
||||
- **Vitest** — тестирование.
|
||||
- `Vitest` — тестирование.
|
||||
|
||||
#### Стили
|
||||
- **PostCSS Modules** — изоляция стилей.
|
||||
- **Mobile First** — подход к адаптивной верстке.
|
||||
- **clsx** — конкатенация CSS‑классов.
|
||||
- `PostCSS Modules` — изоляция стилей.
|
||||
- `Mobile First` — подход к адаптивной верстке.
|
||||
- `clsx` — конкатенация CSS‑классов.
|
||||
|
||||
#### Генерация
|
||||
- **@gromlab/create** — шаблонизатор для создания слоёв и других файлов из шаблонов.
|
||||
- `@gromlab/create` — шаблонизатор для создания слоёв и других файлов из шаблонов.
|
||||
|
||||
<!-- /basics/naming -->
|
||||
## Именование
|
||||
|
||||
Этот раздел описывает соглашения об именовании в проекте. Единые правила делают код предсказуемым и упрощают навигацию по проекту.
|
||||
|
||||
### Базовые правила
|
||||
|
||||
| Что | Рекомендуется |
|
||||
| ---------------- | ---------------------- |
|
||||
| Папки | `kebab-case` |
|
||||
| Файлы | `kebab-case` |
|
||||
| Переменные | `camelCase` |
|
||||
| Константы | `SCREAMING_SNAKE_CASE` |
|
||||
| Классы | `PascalCase` |
|
||||
| React-компоненты | `PascalCase` |
|
||||
| Хуки | `useSomething` |
|
||||
| CSS классы | `camelCase` |
|
||||
|
||||
|
||||
### Именование файлов
|
||||
|
||||
Суффикс обозначает роль или тип файла. Пишется в единственном числе.
|
||||
Формат: `name.<suffix>.ts`.
|
||||
|
||||
**Хуки**
|
||||
- `use-name.hook.ts` — файл хука, функция именуется `useName`
|
||||
|
||||
**Логика**
|
||||
- `.store.ts` — стор
|
||||
- `.service.ts` — сервис
|
||||
|
||||
**Типы и контракты**
|
||||
- `.type.ts` — типы и интерфейсы
|
||||
- `.interface.ts` — интерфейсы
|
||||
- `.enum.ts` — enum
|
||||
- `.dto.ts` — внешние DTO
|
||||
- `.schema.ts` — схемы валидации
|
||||
- `.constant.ts` — константы
|
||||
- `.config.ts` — конфигурация
|
||||
|
||||
**Утилиты**
|
||||
- `.util.ts` — утилиты
|
||||
- `.helper.ts` — вспомогательные функции
|
||||
- `.lib.ts` — библиотечный код
|
||||
|
||||
**Тесты**
|
||||
- `.test.ts` — тесты
|
||||
- `.mock.ts` — моки
|
||||
|
||||
**Хорошо**
|
||||
```text
|
||||
features/
|
||||
└── auth-by-email/
|
||||
├── ui/
|
||||
│ └── login-form.tsx
|
||||
├── hooks/
|
||||
│ └── use-auth.hook.ts
|
||||
├── stores/
|
||||
│ └── auth.store.ts
|
||||
├── types/
|
||||
│ └── auth.interface.ts
|
||||
├── auth-by-email.feature.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```text
|
||||
features/
|
||||
└── authByEmail/
|
||||
├── LoginForm.tsx
|
||||
├── useAuth.ts
|
||||
├── authStore.ts
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
### Булевы значения
|
||||
|
||||
- Использовать префиксы `is`, `has`, `can`, `should`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const isReady = true;
|
||||
const hasAccess = false;
|
||||
const canSubmit = true;
|
||||
const shouldRedirect = false;
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: неясное булево значение без префикса.
|
||||
const ready = true;
|
||||
const access = false;
|
||||
const submit = true;
|
||||
```
|
||||
|
||||
### События и обработчики
|
||||
|
||||
- Обработчики начинать с `handle`.
|
||||
- События и колбэки начинать с `on`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const handleSubmit = () => { ... };
|
||||
const onSubmit = () => { ... };
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: неочевидное назначение имени.
|
||||
const submitClick = () => { ... };
|
||||
```
|
||||
|
||||
### Коллекции
|
||||
|
||||
- Для массивов использовать имена во множественном числе.
|
||||
- Для словарей/мап — использовать суффиксы `ById`, `Map`, `Dict`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const users = [];
|
||||
const usersById = {} as Record<string, User>;
|
||||
const userIds = ['u1', 'u2'];
|
||||
const ordersMap = new Map<string, Order>();
|
||||
const featureFlagsDict = { beta: true, legacy: false } as Record<string, boolean>;
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: имя не отражает, что это коллекция.
|
||||
const user = [];
|
||||
// Плохо: словарь назван как массив.
|
||||
const usersMap = [];
|
||||
// Плохо: по имени непонятно, что это словарь.
|
||||
const users = {} as Record<string, User>;
|
||||
```
|
||||
|
||||
<!-- /basics/architecture -->
|
||||
## Архитектура
|
||||
|
||||
Архитектура построена на FSD (`Feature‑Sliced Design`) и строгих границах модулей.
|
||||
Цель — разделить ответственность, упростить сопровождение и контроль зависимостей.
|
||||
Этот раздел описывает архитектуру проекта: из каких слоёв состоит приложение,
|
||||
как организован код внутри слоёв и какие правила управляют зависимостями.
|
||||
|
||||
### Принципы
|
||||
### Что важно знать
|
||||
|
||||
- Разделять UI, бизнес-логику и инфраструктуру.
|
||||
- Держать зависимости однонаправленными.
|
||||
- Открывать наружу только публичный API модулей.
|
||||
- Не допускать циклических зависимостей.
|
||||
Проект использует [FSD (Feature-Sliced Design)](https://feature-sliced.design/docs/get-started/overview)
|
||||
как базовую архитектурную методологию. Если вы не знакомы с FSD — начните с официальной документации.
|
||||
|
||||
### Слои (FSD)
|
||||
Данная архитектура является **надстройкой над FSD**, а не заменой. Все правила FSD действуют
|
||||
по умолчанию — если правило явно не переопределено в этом документе, применяется стандарт FSD.
|
||||
Единственное отклонение: вместо слайсов используются **компоненты**.
|
||||
|
||||
- **app** — инициализация приложения: провайдеры, глобальные стили. В Next.js эта же папка `app/` дополнительно содержит системные файлы роутинга (`layout.tsx`, `page.tsx`).
|
||||
- **screens** — UI-компоненты страниц. Каждый экран — отдельный компонент, который собирает виджеты и фичи конкретной страницы. Роутинг только использует эти компоненты — он не является частью слоя `screens`. В Next.js файлы `page.tsx` остаются тонкими: импортируют экран и рендерят его.
|
||||
- **layouts** — каркас и шаблоны страниц.
|
||||
- **widgets** — крупные блоки интерфейса, собирающие несколько сценариев.
|
||||
- **features** — отдельные пользовательские действия и сценарии.
|
||||
- **entities** — бизнес-сущности и их модель.
|
||||
- **shared** — переиспользуемая инфраструктура, утилиты и базовые UI‑компоненты.
|
||||
### Слои
|
||||
|
||||
### Модули (FSD)
|
||||
| Слой | Назначение |
|
||||
|------|-----------|
|
||||
| `app` | Инициализация: провайдеры, стили, роутинг Next.js |
|
||||
| `screens` | Сборка страницы из виджетов и фич |
|
||||
| `layouts` | Каркасы и шаблоны страниц |
|
||||
| `widgets` | Крупные блоки интерфейса |
|
||||
| `features` | Пользовательские сценарии и действия |
|
||||
| `entities` | Бизнес-сущности |
|
||||
| `shared` | Утилиты, UI-кит, инфраструктура |
|
||||
|
||||
- Модуль — это отдельная папка в слоях `screens`, `layouts`, `widgets`, `features`, `entities`, которая реализует один сценарий/блок. В корне модуля лежит главный файл (`*.screen.tsx`, `*.layout.tsx`, `*.widget.tsx`, `*.feature.tsx`, `*.entity.tsx`) и публичный API (`index.ts`).
|
||||
- Внутри модуля используются подпапки (по необходимости):
|
||||
- `ui/` — дочерние UI‑компоненты модуля.
|
||||
- `model/` — состояние и бизнес‑логика модуля.
|
||||
- `styles/` — локальные стили модуля.
|
||||
- `helpers/` — локальные хелперы.
|
||||
- `lib/` — утилиты модуля.
|
||||
- `api/` — API‑вызовы модуля.
|
||||
Слой `pages` не используется — конфликтует с Next.js. Вместо него: `screens` и `layouts`.
|
||||
|
||||
### Правила зависимостей
|
||||
Зависимости идут строго сверху вниз: `app → screens → layouts → widgets → features → entities → shared`.
|
||||
|
||||
- Допустимые импорты идут сверху вниз: `app → screens → layouts → widgets → features → entities → shared`.
|
||||
- Импорты между слоями — через публичный API.
|
||||
- Внутри одного слоя — относительные импорты.
|
||||
### Компоненты
|
||||
|
||||
### Публичный API модулей
|
||||
Компонент — стандартная UI-единица, такая же как в любом React-проекте. Содержит корневой `.tsx`,
|
||||
публичный API (`index.ts`) и сегменты.
|
||||
|
||||
- Каждый модуль экспортирует наружу только то, что нужно другим слоям.
|
||||
- Внешние импорты идут только через `index`‑файл модуля.
|
||||
- Внутренние файлы не импортируются напрямую извне.
|
||||
Компоненты располагаются в:
|
||||
- `shared/ui/` — переиспользуемые компоненты без бизнес-контекста
|
||||
- `ui/` внутри master component'а — дочерние компоненты *(подробнее в разделе [Master component](#master-component))*
|
||||
|
||||
### Границы ответственности
|
||||
### Сегменты
|
||||
|
||||
- Бизнес‑логика не размещается в UI‑компонентах.
|
||||
- UI‑компоненты должны быть максимально простыми и предсказуемыми.
|
||||
- Связь между независимыми сценариями поднимается на уровень выше.
|
||||
Сегмент — папка внутри компонента, группирующая код по техническому назначению. Набор не фиксирован.
|
||||
|
||||
### Типовые ошибки
|
||||
| Сегмент | Назначение |
|
||||
|---------|-----------|
|
||||
| `styles/` | Стили |
|
||||
| `types/` | Интерфейсы, типы, enums, DTO |
|
||||
| `ui/` | Компоненты, провайдеры и любые другие элементы интерфейса |
|
||||
| `stores/` | Сторы состояния |
|
||||
| `hooks/` | React-хуки |
|
||||
| `services/` | Внешние источники данных |
|
||||
| `lib/` | Утилиты |
|
||||
| `helpers/` | Вспомогательные функции |
|
||||
| `config/` | Константы, конфигурация |
|
||||
|
||||
- Импорт из более высокого слоя в более низкий.
|
||||
- Смешивание логики нескольких слоёв в одном модуле.
|
||||
- Прямые импорты внутренних файлов, минуя публичный API.
|
||||
### Master component
|
||||
|
||||
Master component — это обычный компонент, на который наложен ряд дополнительных правил.
|
||||
Эти правила определяют его место в архитектуре и границы зависимостей.
|
||||
|
||||
- Может располагаться только в слоях: `screens`, `layouts`, `widgets`, `features`, `entities`
|
||||
- Импортирует master component'ы только из слоёв ниже по иерархии
|
||||
- Корневой `.tsx` именуется с суффиксом слоя: `header.widget.tsx`, `auth.feature.tsx`
|
||||
- Корневой `.tsx` необязателен — `index.ts` может экспортировать несколько сущностей напрямую
|
||||
- Дочерние компоненты в `ui/` доступны снаружи только через `index.ts`
|
||||
- Компоненты внутри одного `ui/` могут импортировать друг друга
|
||||
|
||||
<!-- /basics/code-style -->
|
||||
## Стиль кода
|
||||
@@ -399,166 +537,10 @@ const options = { id: 1,name: 'User' };
|
||||
const config = { url: '/api/users', method: 'GET', params: { page: 1, pageSize: 20 } };
|
||||
```
|
||||
|
||||
<!-- /basics/naming -->
|
||||
## Именование
|
||||
|
||||
Именование должно быть предсказуемым, коротким и отражать смысл сущности.
|
||||
|
||||
### Базовые правила
|
||||
|
||||
| Что | Рекомендуется |
|
||||
| ---------------- | ---------------------- |
|
||||
| Папки | `kebab-case` |
|
||||
| Файлы | `kebab-case` |
|
||||
| Переменные | `camelCase` |
|
||||
| Константы | `SCREAMING_SNAKE_CASE` |
|
||||
| Классы | `PascalCase` |
|
||||
| React-компоненты | `PascalCase` |
|
||||
| Хуки | `useSomething` |
|
||||
| CSS классы | `camelCase` |
|
||||
|
||||
|
||||
### Архитектурный неймспейс
|
||||
|
||||
Соглашение о суффиксах, которые обозначают слой (уровень абстракции), роль или тип файла.
|
||||
|
||||
- Суффиксы используются для обозначения слоя, роли или типа файла.
|
||||
- Суффиксы всегда пишутся в единственном числе.
|
||||
- Формат имени: `name.<suffix>.ts` или `name.<suffix>.tsx`.
|
||||
|
||||
**UI и слои FSD**
|
||||
- `.screen.tsx` — экран
|
||||
- `.layout.tsx` — layout
|
||||
- `.widget.tsx` — виджет
|
||||
- `.feature.tsx` — UI фичи
|
||||
- `.entity.tsx` — UI сущности
|
||||
|
||||
Остальные `.tsx` файлы (компоненты в `shared/ui/`, дочерние компоненты в `ui/`) не помечаются суффиксами — расширение `.tsx` само по себе означает UI‑компонент.
|
||||
|
||||
**Логика и модель**
|
||||
- `.store.ts` — стор
|
||||
- `.service.ts` — сервис
|
||||
|
||||
**Типы и контракты**
|
||||
- `.type.ts` — типы и интерфейсы
|
||||
- `.interface.ts` — файл с интерфейсами (если нужен отдельный контракт)
|
||||
- `.enum.ts` — enum
|
||||
- `.dto.ts` — внешние DTO
|
||||
- `.schema.ts` — схемы валидации
|
||||
- `.constant.ts` — константы
|
||||
- `.config.ts` — конфигурация
|
||||
|
||||
**Утилиты и хелперы**
|
||||
- `.util.ts` — утилиты
|
||||
- `.helper.ts` — вспомогательные функции
|
||||
- `.lib.ts` — вспомогательные функции
|
||||
|
||||
**Тесты**
|
||||
- `.test.ts` / `.test.tsx`
|
||||
- `.mock.ts`
|
||||
|
||||
|
||||
**Хорошо**
|
||||
```text
|
||||
src/
|
||||
├── screens/
|
||||
│ └── main/
|
||||
│ ├── main.screen.tsx
|
||||
│ └── index.ts
|
||||
├── features/
|
||||
│ └── auth-by-email/
|
||||
│ ├── ui/
|
||||
│ │ └── login-form.tsx
|
||||
│ ├── auth-by-email.feature.tsx
|
||||
│ └── index.ts
|
||||
└── shared/
|
||||
└── ui/
|
||||
└── icon/
|
||||
├── styles/
|
||||
│ └── icon.module.css
|
||||
├── types/
|
||||
│ └── icon.interface.ts
|
||||
├── icon.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```text
|
||||
// Плохо: нет единых правил для слоёв и публичных файлов.
|
||||
src/
|
||||
├── screens/
|
||||
│ └── Main/
|
||||
│ └── Main.tsx
|
||||
└── features/
|
||||
└── authByEmail/
|
||||
└── login-form.tsx
|
||||
```
|
||||
|
||||
### Булевы значения
|
||||
|
||||
- Использовать префиксы `is`, `has`, `can`, `should`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const isReady = true;
|
||||
const hasAccess = false;
|
||||
const canSubmit = true;
|
||||
const shouldRedirect = false;
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: неясное булево значение без префикса.
|
||||
const ready = true;
|
||||
const access = false;
|
||||
const submit = true;
|
||||
```
|
||||
|
||||
### События и обработчики
|
||||
|
||||
- Обработчики начинать с `handle`.
|
||||
- События и колбэки начинать с `on`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const handleSubmit = () => { ... };
|
||||
const onSubmit = () => { ... };
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: неочевидное назначение имени.
|
||||
const submitClick = () => { ... };
|
||||
```
|
||||
|
||||
### Коллекции
|
||||
|
||||
- Для массивов использовать имена во множественном числе.
|
||||
- Для словарей/мап — использовать суффиксы `ById`, `Map`, `Dict`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const users = [];
|
||||
const usersById = {} as Record<string, User>;
|
||||
const userIds = ['u1', 'u2'];
|
||||
const ordersMap = new Map<string, Order>();
|
||||
const featureFlagsDict = { beta: true, legacy: false } as Record<string, boolean>;
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: имя не отражает, что это коллекция.
|
||||
const user = [];
|
||||
// Плохо: словарь назван как массив.
|
||||
const usersMap = [];
|
||||
// Плохо: по имени непонятно, что это словарь.
|
||||
const users = {} as Record<string, User>;
|
||||
```
|
||||
|
||||
<!-- /basics/documentation -->
|
||||
## Документирование
|
||||
|
||||
Документирование должно помогать понять назначение сущности, а не дублировать её типы или очевидные детали.
|
||||
Этот раздел описывает правила документирования кода: когда и как писать комментарии к функциям, компонентам, типам и интерфейсам.
|
||||
|
||||
### Правила
|
||||
|
||||
@@ -620,8 +602,7 @@ export enum TodoFilter {
|
||||
<!-- /basics/typing -->
|
||||
## Типизация
|
||||
|
||||
Типизация обязательна для всех публичных интерфейсов, функций и компонентов.
|
||||
Цель — предсказуемость, безопасность и автодополнение.
|
||||
Этот раздел описывает правила типизации: как типизировать компоненты, функции и работу с `any`/`unknown`.
|
||||
|
||||
### Общие правила
|
||||
|
||||
|
||||
Reference in New Issue
Block a user