From bf1781f1438debe30c3cd932021dcb5c2f9ca5e8 Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Sun, 3 May 2026 04:22:26 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=BF=D0=B5=D1=80=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D1=82=D1=8C=20=D0=B0=D1=80=D1=85=D0=B8?= =?UTF-8?q?=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80=D1=83=20=D0=B8=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=BA=D0=BB=D0=B0=D0=B4=D0=BD=D1=8B=D0=B5=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D1=8B=20=D0=BA=D0=BE=D0=BC=D0=BF?= =?UTF-8?q?=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=B0=20=D0=B8=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D1=83=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - обновлена каноническая спецификация SLM Design из slm-design.gromlab.ru - файлы архитектуры перенесены из reference/ в basics/architecture/ - добавлена предупреждающая плашка о локальной копии архитектуры - infrastructure/ переименован в infra/ по канону спецификации - прикладной раздел «Компонент» переписан: определение заменено ссылкой на архитектуру, добавлен пример UserStatus с типами, стилями и JSDoc - прикладной раздел «Модуль» переписан: показаны три типа модулей (UI, бизнес, инфраструктурный), добавлен пример фабрики бизнес-модуля - добавлен запрет ручного создания компонентов: только через кодогенератор - обновлён раздел типизации: возвращаемый тип React-компонентов не указывается - обновлён шаблон компонента в templates-create: суффикс -props.type.ts - обновлены ссылки в page-level, project-structure, aliases - сгенерированы llms.txt, llms-full.txt, README и публичные копии --- .vitepress/config.ts | 6 +- docs/docs/DEVELOP.md | 6 +- docs/docs/MAP.md | 6 +- docs/docs/applied/aliases.md | 2 +- docs/docs/applied/component.md | 258 +++++++--------- docs/docs/applied/module.md | 234 +++++++-------- docs/docs/applied/page-level.md | 2 +- docs/docs/applied/project-structure.md | 2 +- .../applied/templates/templates-create.md | 8 +- .../docs/applied/templates/templates-usage.md | 6 + docs/docs/basics/architecture/index.md | 24 +- .../architecture/{reference => }/layers.md | 34 +-- docs/docs/basics/architecture/modules.md | 284 ++++++++++++++++++ .../basics/architecture/reference/modules.md | 165 ---------- .../architecture/{reference => }/segments.md | 47 ++- docs/docs/basics/typing.md | 7 +- 16 files changed, 603 insertions(+), 488 deletions(-) rename docs/docs/basics/architecture/{reference => }/layers.md (83%) create mode 100644 docs/docs/basics/architecture/modules.md delete mode 100644 docs/docs/basics/architecture/reference/modules.md rename docs/docs/basics/architecture/{reference => }/segments.md (65%) diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 8b826d3..01d22b1 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -19,9 +19,9 @@ const sidebar = [ collapsed: true, items: [ { text: 'Обзор', link: '/docs/basics/architecture/' }, - { text: 'Слои', link: '/docs/basics/architecture/reference/layers' }, - { text: 'Модули', link: '/docs/basics/architecture/reference/modules' }, - { text: 'Сегменты', link: '/docs/basics/architecture/reference/segments' }, + { text: 'Слои', link: '/docs/basics/architecture/layers' }, + { text: 'Модули', link: '/docs/basics/architecture/modules' }, + { text: 'Сегменты', link: '/docs/basics/architecture/segments' }, ], }, { text: 'Стиль кода', link: '/docs/basics/code-style' }, diff --git a/docs/docs/DEVELOP.md b/docs/docs/DEVELOP.md index 265090d..523abfd 100644 --- a/docs/docs/DEVELOP.md +++ b/docs/docs/DEVELOP.md @@ -22,9 +22,9 @@ description: Что AI-агент обязан прочитать перед н ### 1. Архитектура (КРИТИЧЕСКИ ВАЖНО) * [Архитектура: Обзор](./basics/architecture/index.md) -* [Архитектура: Слои](./basics/architecture/reference/layers.md) -* [Архитектура: Модули](./basics/architecture/reference/modules.md) -* [Архитектура: Сегменты](./basics/architecture/reference/segments.md) +* [Архитектура: Слои](./basics/architecture/layers.md) +* [Архитектура: Модули](./basics/architecture/modules.md) +* [Архитектура: Сегменты](./basics/architecture/segments.md) **Архитектура — это самое важное в проекте.** diff --git a/docs/docs/MAP.md b/docs/docs/MAP.md index 2f022c8..9cb1c18 100644 --- a/docs/docs/MAP.md +++ b/docs/docs/MAP.md @@ -12,9 +12,9 @@ - [Технологии и библиотеки](./basics/tech-stack.md) — Какие библиотеки и инструменты используются в проекте. - [Именование](./basics/naming.md) — Как называть переменные, файлы и прочие сущности в коде. - [Архитектура: Обзор](./basics/architecture/index.md) — Архитектурный подход проекта: что такое SLM и как он устроен. -- [Архитектура: Слои](./basics/architecture/reference/layers.md) — Из каких слоёв состоит SLM-архитектура и как они связаны. -- [Архитектура: Модули](./basics/architecture/reference/modules.md) — Что такое модуль в SLM-архитектуре и как он устроен. -- [Архитектура: Сегменты](./basics/architecture/reference/segments.md) — Что такое сегмент модуля в SLM-архитектуре и какие они бывают. +- [Архитектура: Слои](./basics/architecture/layers.md) — Из каких слоёв состоит SLM-архитектура и как они связаны. +- [Архитектура: Модули](./basics/architecture/modules.md) — Что такое модуль в SLM-архитектуре и как он устроен. +- [Архитектура: Сегменты](./basics/architecture/segments.md) — Что такое сегмент модуля в SLM-архитектуре и какие они бывают. - [Стиль кода](./basics/code-style.md) — Как оформляется код в проекте. - [Документирование](./basics/documentation.md) — Что и как документировать в коде. - [Типизация](./basics/typing.md) — Как типизируется код в проекте. diff --git a/docs/docs/applied/aliases.md b/docs/docs/applied/aliases.md index 2fcb6c1..056e7e3 100644 --- a/docs/docs/applied/aliases.md +++ b/docs/docs/applied/aliases.md @@ -36,7 +36,7 @@ keywords: [алиасы, aliases, paths, tsconfig, импорты, baseUrl, app, - **Каждый импорт между модулями — через алиас слоя.** Относительные пути (`../../`) запрещены за пределами своего модуля. - **Внутри одного модуля** допустимы относительные импорты (`./model`, `./ui/button`) — это часть инкапсуляции модуля. - **Префикс `@/` не используется.** Имя слоя — само по себе адрес. -- **Направление импортов** определяется архитектурой, не алиасами. Алиас разрешает импорт технически, но не отменяет правила слоёв (→ [Слои](/docs/basics/architecture/reference/layers)). +- **Направление импортов** определяется архитектурой, не алиасами. Алиас разрешает импорт технически, но не отменяет правила слоёв (→ [Слои](/docs/basics/architecture/layers)). **Хорошо** diff --git a/docs/docs/applied/component.md b/docs/docs/applied/component.md index 4e825c3..da8f0b8 100644 --- a/docs/docs/applied/component.md +++ b/docs/docs/applied/component.md @@ -1,201 +1,165 @@ --- title: Компонент -description: Как создавать React-компоненты внутри SLM-модулей. +description: Как должен выглядеть сгенерированный React-компонент внутри SLM-модуля. --- # Компонент -Как создавать React-компоненты внутри SLM-модулей. +Как должен выглядеть сгенерированный React-компонент внутри SLM-модуля. ## Назначение -Компонент — минимальная UI-единица проекта. Это один `.tsx` файл без собственной папки, сегментов и публичного API. +Архитектурное определение компонента описано в разделе [Модули → Компонент](/docs/basics/architecture/modules#компонент), а структура сегмента `ui/` — в разделе [Сегменты → ui/](/docs/basics/architecture/segments#сегмент-ui). -Компонент может использовать стили, типы, хуки и другие ресурсы родительского модуля. Наличие связанных файлов в `styles/` или `types/` не превращает компонент в модуль. +Эта страница не повторяет архитектурные ограничения. Она показывает, каким должен быть результат генерации компонента: структура папки, `.tsx`, типы, стили и локальный экспорт. -## Компонент или модуль +::: danger Компоненты не создаются вручную +Компоненты в проекте создаются только через кодогенератор: через [VS Code](/docs/applied/templates/templates-usage#через-vs-code) или [CLI](/docs/applied/templates/templates-usage#через-cli). -Классификация определяется границей владения: +Ручное создание компонента запрещено. Это грубое нарушение правил работы в проекте для разработчика и AI-ассистента. -- `component` — один `.tsx` файл внутри модуля; -- `module` — папка с `index.ts`, сегментами и собственной публичной границей. +Если в проекте нет шаблона `.templates/component`, сначала создайте шаблон по разделу [Создание шаблонов](/docs/applied/templates/templates-create), и только потом генерируйте компонент на его основе. +::: + +## Создание + +1. Проверьте, что в проекте есть шаблон `.templates/component`. +2. Если шаблона нет — создайте его по разделу [Создание шаблонов](/docs/applied/templates/templates-create). +3. Сгенерируйте компонент через [VS Code или CLI](/docs/applied/templates/templates-usage). + +Структура и код ниже показывают ожидаемый результат генерации. Их нельзя использовать как инструкцию для ручного создания файлов. + +## Структура + +Компонент размещается в `ui/{component-name}/` родительского модуля. + +Для каждого компонента обязательны `.tsx`, типы, стили и локальный `index.ts`. ```text -user/ -├── ui/ -│ └── user-avatar.tsx # компонент -├── styles/ -│ └── user-avatar.module.css # ресурс родительского модуля -├── types/ -│ └── user-avatar.type.ts # ресурс родительского модуля -└── user.tsx # корневой компонент модуля -``` - -`user-avatar.tsx` остаётся компонентом, потому что у него нет собственной папки, `index.ts` и сегментов. - -## Где размещать - -Компонент размещается внутри модуля: - -- В корне модуля, если это главная UI-сущность модуля. -- В `ui/`, если это вспомогательный компонент модуля. - -```text -user/ -├── ui/ -│ └── user-avatar.tsx -├── styles/ -│ ├── user.module.css -│ └── user-avatar.module.css -├── types/ -│ ├── user.type.ts -│ └── user-avatar.type.ts -├── user.tsx -└── index.ts -``` - -`user.tsx` — корневой компонент модуля. `ui/user-avatar.tsx` — вспомогательный компонент этого же модуля. - -## Что запрещено - -- Заворачивать компонент в папку: `ui/header/header.tsx`. -- Создавать для компонента отдельный `index.ts`. -- Создавать собственные сегменты внутри папки компонента: `ui/header/styles/`, `ui/header/types/`, `ui/header/hooks/` и т.п. -- Декларировать внутри `.tsx` сторы, сервисы, API-клиенты, мапперы или утилиты. Для этого есть сегменты родительского модуля. -- Размещать бизнес-правила прямо в компоненте. Компонент может использовать готовые зависимости модуля, но не определяет их. -- Размещать компонент в `parts/` напрямую. `parts/` содержит только модули. - -**Плохо** - -```text -user/ +user-card/ └── ui/ - └── user-avatar/ + └── user-status/ ├── styles/ - │ └── user-avatar.module.css - ├── user-avatar.tsx + │ └── user-status.module.css + ├── types/ + │ └── user-status-props.type.ts + ├── user-status.tsx └── index.ts ``` -**Хорошо** - -```text -user/ -├── ui/ -│ └── user-avatar.tsx -├── styles/ -│ └── user-avatar.module.css -└── types/ - └── user-avatar.type.ts -``` - -## Стили и типы - -Компонент использует ресурсы родительского модуля. - -`styles/` и `types/` рядом с корневым компонентом — это сегменты модуля, а не собственные папки `.tsx` файла. - -- CSS Module компонента лежит в `styles/` родительского модуля и называется по компоненту: `user-avatar.module.css`. -- Если у компонента есть CSS Module, его корневой класс всегда называется `.root`. -- Типы компонента лежат в `types/` родительского модуля и называются по компоненту: `user-avatar.type.ts`. -- Локальный `type Props` внутри `.tsx` не используется. Типы пропсов всегда выносятся в `types/` родительского модуля. -- Экспорт типа из `types/` не делает его публичным API. Публичным он становится только при реэкспорте из `index.ts` модуля. - -Причина `.root`: в DevTools проще находить корневой DOM-узел компонента и отличать его от внутренних элементов. - ## Реализация -- Компоненты объявляются через `const`. -- `React.FC` не используется. -- JSDoc-комментарий обязателен для компонента. -- Пропсы деструктурируются в теле компонента. -- `className` объединяется с `styles.root` через `cl()`. -- Побочные эффекты и состояние выносятся в хуки модуля, если перестают быть тривиальными. -- Компонент возвращает JSX и не содержит orchestration-код страницы или бизнес-домена. +Пример ниже показывает файлы базового компонента. -`user/types/user-avatar.type.ts` +### Типы + +Файл типов делится на три части: + +- `UserStatusParams` — собственные параметры компонента. Здесь лежат только данные, которые нужны именно этому компоненту. +- `RootAttrs` — параметры корневой обёртки: `div`, `span`, `a`, `button` или другого HTML-элемента. Если компонент сам управляет `children`, они исключаются через `Omit`. +- `UserStatusProps` — итоговые пропсы компонента. Тип объединяет собственные параметры и параметры корневой обёртки. + +Собственные параметры и их поля документируются по правилам раздела [Документирование → Типы, интерфейсы, enum](/docs/basics/documentation#типы-интерфейсы-enum). + +`user-card/ui/user-status/types/user-status-props.type.ts` ```ts -import type { ImageProps } from 'next/image' +import type { ComponentPropsWithoutRef } from 'react' /** - * Параметры UserAvatar. + * Параметры UserStatus. */ -export type UserAvatarParams = {} +export type UserStatusParams = { + /** Текст статуса пользователя. */ + label: string + /** Доступен ли пользователь сейчас. */ + isOnline: boolean +} -/** Пропсы базового изображения. */ -type RootAttrs = ImageProps +/** Атрибуты корневого элемента без children. */ +type RootAttrs = Omit, 'children'> -export type UserAvatarProps = RootAttrs & UserAvatarParams +export type UserStatusProps = RootAttrs & UserStatusParams ``` -`user/ui/user-avatar.tsx` +### TSX + +В `.tsx` лежит только сам компонент: + +- Компонент объявляется через `const` и именованный экспорт. +- `React.FC` не используется. +- Параметры компонента типизируются через `Props`. +- Возвращаемый тип не указывается: TypeScript корректно выводит JSX-результат, а явный `ReactElement` сужает допустимые варианты возврата. +- JSDoc-комментарий обязателен и пишется по правилам раздела [Документирование → Компоненты](/docs/basics/documentation#компоненты). +- Пропсы деструктурируются в теле компонента, а не в сигнатуре. +- Из пропсов обязательно выделяются `className` и `...rootAttrs`. +- Функция конкатенации CSS-классов импортируется и именуется `cl`. +- Корневой CSS-класс всегда называется `.root`. + +Комментарий описывает назначение и сценарии применения компонента, а не DOM-разметку или внутреннюю реализацию. + +`className` — внешний CSS-класс, который родитель может передать компоненту. `rootAttrs` — остальные атрибуты корневой обёртки: `id`, `aria-*`, `data-*`, обработчики событий и другие HTML-атрибуты. Они прокидываются на корневой DOM-элемент компонента. + +`.root` нужен, чтобы в DevTools быстро находить корневой DOM-узел компонента и одинаково подключать внешний `className` к реальному корню. + +`user-card/ui/user-status/user-status.tsx` ```tsx import cl from 'clsx' -import Image from 'next/image' -import type { UserAvatarProps } from '../types/user-avatar.type' -import styles from '../styles/user-avatar.module.css' +import type { UserStatusProps } from './types/user-status-props.type' +import styles from './styles/user-status.module.css' /** - * Аватар пользователя. + * Статус пользователя в карточке профиля. * * Используется для: - * - отображения пользователя в карточке - * - отображения пользователя в шапке профиля + * - отображения текущей доступности пользователя + * - визуального выделения онлайн- и офлайн-состояний */ -export const UserAvatar = (props: UserAvatarProps) => { - const { className, ...imageProps } = props +export const UserStatus = (props: UserStatusProps) => { + const { label, isOnline, className, ...rootAttrs } = props - return + return ( + + {label} + + ) } ``` -`user/styles/user-avatar.module.css` +### Стили + +`user-card/ui/user-status/styles/user-status.module.css` ```css .root { - display: block; + display: inline-flex; + align-items: center; + gap: 6px; + color: var(--color-text-muted); +} + +.root::before { + content: ''; + width: 6px; + height: 6px; border-radius: 50%; + background: currentColor; +} + +.online { + color: var(--color-success); } ``` -## Когда нужен модуль +### Локальный экспорт -Решение о выделении модуля остаётся за разработчиком. Поднимать компонент в модуль стоит, если он становится самостоятельной областью: +`user-card/ui/user-status/index.ts` -- получает свои вложенные компоненты; -- получает свои хуки, стор или сервисы; -- получает внутренние мапперы или утилиты; -- требует собственного публичного API; -- начинает переиспользоваться вне родительского модуля; -- становится отдельной зоной параллельной разработки. - -Пример: страница — это screen-модуль, а самостоятельные секции страницы — вложенные модули в `parts/`. - -```text -screens/home/ -├── parts/ -│ ├── hero-section/ -│ │ ├── styles/ -│ │ │ └── hero-section.module.css -│ │ ├── types/ -│ │ │ └── hero-section.type.ts -│ │ ├── hero-section.tsx -│ │ └── index.ts -│ └── features-section/ -│ ├── styles/ -│ │ └── features-section.module.css -│ ├── types/ -│ │ └── features-section.type.ts -│ ├── features-section.tsx -│ └── index.ts -├── styles/ -│ └── home.module.css -├── types/ -│ └── home.type.ts -├── home.screen.tsx -└── index.ts +```ts +export { UserStatus } from './user-status' +export type { UserStatusProps } from './types/user-status-props.type' ``` - -`hero-section` и `features-section` — модули, потому что это самостоятельные части страницы со своей структурой и публичной точкой входа. diff --git a/docs/docs/applied/module.md b/docs/docs/applied/module.md index d063468..94f21ac 100644 --- a/docs/docs/applied/module.md +++ b/docs/docs/applied/module.md @@ -1,162 +1,156 @@ --- title: Модуль -description: Как создавать и организовывать SLM-модули в проекте. +description: Как должен выглядеть сгенерированный SLM-модуль в проекте. --- # Модуль -Как создавать и организовывать SLM-модули в проекте. +Как должен выглядеть сгенерированный SLM-модуль в проекте. ## Назначение -Модуль — основной строительный блок SLM-архитектуры. Это папка с публичным API (`index.ts`) и опциональными сегментами: компонентами, стилями, типами, хуками, сторами, сервисами и вложенными модулями. +Архитектурное определение модуля описано в разделе [Архитектура → Модули](/docs/basics/architecture/modules). Список сегментов описан в разделе [Архитектура → Сегменты](/docs/basics/architecture/segments). -Если UI-сущность остаётся одним `.tsx` файлом и использует ресурсы родительского модуля — это [компонент](/docs/applied/component), а не модуль. Связанные файлы в `styles/` и `types/` родителя не создают новую модульную границу. +Эта страница показывает прикладное оформление трёх типов модулей: UI, бизнес и инфраструктурный. -Архитектурное определение: [Модули SLM](/docs/basics/architecture/reference/modules). Список сегментов: [Сегменты SLM](/docs/basics/architecture/reference/segments). +## Создание -## Когда нужен модуль +1. Проверьте, что в проекте есть нужный шаблон в `.templates/`. +2. Если шаблона нет — создайте его по разделу [Создание шаблонов](/docs/applied/templates/templates-create). +3. Сгенерируйте модуль через [VS Code или CLI](/docs/applied/templates/templates-usage). -Создавайте модуль, если сущности нужны: +## Типы модулей -- публичный API; -- хуки, сторы, сервисы, мапперы или утилиты; -- вложенные части; -- переиспользование вне родительского модуля; -- самостоятельная ответственность на слое. +Архитектура определяет три типа модулей ([Типы модулей](/docs/basics/architecture/modules#типы-модулей)): -Если понадобилась папка вокруг компонента — это сигнал, что нужен модуль. +| Тип | Обязательный файл | Описание | +|---|---|---| +| UI-модуль | `{name}.tsx` | Модуль, выросший из компонента | +| Бизнес-модуль | `{name}.factory.ts` | Модуль вокруг публичного runtime API | +| Инфраструктурный модуль | нет | Модуль вокруг технического сервиса | -## Где размещать +## UI-модуль -Модуль размещается на самом низком уровне использования. +UI-модуль — это компонент, который перерос ограничения компонента: получил собственные хуки, вложенные модули в `parts/`, сценарную логику или публичный API. Внутренняя структура та же, что у компонента: корневой `.tsx`, типы, стили, `ui/`. Но без ограничений компонента. -- Нужен только одному модулю — размещается в `parts/` родителя. -- Нужен одной странице — размещается в `screens/{name}/parts/`. -- Нужен одному layout — размещается в `layouts/{name}/parts/`. -- Переиспользуется между страницами или layout — поднимается в `widgets/`. -- Представляет бизнес-домен — размещается в `business/`. -- Является UI-китом — размещается в `ui/`. +Подробное оформление компонентов внутри `ui/` описано в разделе [Компонент](/docs/applied/component). -`app/` не содержит модулей. Это слой файлового роутинга и инициализации. +## Бизнес-модуль -## Структура +Бизнес-модуль строится вокруг публичного runtime API. Ключевой файл — фабрика (`{name}.factory.ts`), которая возвращает всё, что нужно внешнему коду в runtime. -Минимальный UI-модуль: +Архитектурное описание фабрики: [Архитектура → Фабрика](/docs/basics/architecture/modules#фабрика). + +### Структура ```text -user/ -├── user.tsx -└── index.ts +business/customer/ +├── customer.factory.ts +├── index.ts +└── types/ + ├── customer.type.ts + ├── customer-api.type.ts + ├── customer-deps.type.ts + └── customer-factory.type.ts ``` -Модуль расширяется сегментами только при реальной потребности: +### Типы -```text -user/ -├── ui/ -│ └── user-avatar.tsx -├── parts/ -│ └── user-posts/ -│ ├── user-posts.tsx -│ └── index.ts -├── styles/ -│ ├── user.module.css -│ └── user-avatar.module.css -├── types/ -│ ├── user.type.ts -│ └── user-avatar.type.ts -├── user.tsx -└── index.ts -``` - -Корневой компонент опционален. Business- и infrastructure-модули могут состоять только из хуков, сервисов, типов и публичного API. - -## `ui/` и `parts/` - -`ui/` содержит только компоненты: отдельные `.tsx` файлы без собственных сегментов. - -`parts/` содержит только модули: каждая запись внутри `parts/` — папка с собственным `index.ts`. Отдельные `.tsx`, стили, хуки или произвольные файлы в `parts/` не кладутся. - -```text -user/ -├── ui/ -│ └── user-avatar.tsx # компонент -└── parts/ - └── user-posts/ # модуль - ├── styles/ - │ └── user-posts.module.css - ├── user-posts.tsx - └── index.ts -``` - -Если компоненту в `ui/` понадобились стили или типы, они добавляются в `styles/` и `types/` родительского модуля. Если компоненту нужны собственные хуки, вложенные части или публичная граница — он переносится в `parts/` как модуль. - -## Публичный API - -`index.ts` — единственная точка входа в модуль. Внешние импорты внутренних файлов запрещены. +`business/customer/types/customer-api.type.ts` ```ts -// user/index.ts -export { User } from './user' -export type { UserProps } from './types/user.type' +export type CustomerApi = { + useCustomer: () => Customer + CustomerCard: (props: CustomerCardProps) => ReactNode +} ``` +`business/order/types/order-deps.type.ts` + ```ts -// Плохо: импорт в обход публичного API. -import { UserPosts } from 'screens/user/parts/user-posts/user-posts' - -// Хорошо: импорт через публичный API родительского модуля. -import { User } from 'screens/user' +export type OrderDeps = { + customer: Pick +} ``` -Вложенный модуль имеет свой `index.ts`, но наружу родителя экспортируется только при необходимости. +`business/order/types/order-factory.type.ts` -## Именование - -Базовые правила описаны в разделе [Именование](/docs/basics/naming). - -- Папка модуля — `kebab-case`: `user-posts/`. -- Файл корневого компонента повторяет имя папки: `user-posts/user-posts.tsx`. -- Корневые модули слоёв наследуют роль слоя в имени файла: `screens/profile/profile.screen.tsx`, `layouts/main/main.layout.tsx`. -- Корневой компонент именуется в `PascalCase`: `UserPosts`. -- Если имя без контекста слишком общее, добавляется префикс родителя или роль слоя: `ProfileUserPosts`, `ProfileScreen`, `MainLayout`. - -## Примеры - -### Screen-модуль - -```text -screens/profile/ -├── ui/ -│ └── profile-heading.tsx -├── parts/ -│ └── activity-feed/ -│ ├── styles/ -│ │ └── activity-feed.module.css -│ ├── activity-feed.tsx -│ └── index.ts -├── styles/ -│ ├── profile.module.css -│ └── profile-heading.module.css -├── types/ -│ ├── profile.type.ts -│ └── profile-heading.type.ts -├── profile.screen.tsx -└── index.ts +```ts +export type OrderFactory = (deps: OrderDeps) => OrderApi ``` -### Business-модуль без корневого компонента +### Фабрика без зависимостей + +`business/customer/customer.factory.ts` + +```ts +import type { CustomerFactory } from './types/customer-factory.type' + +export const customerFactory: CustomerFactory = () => { + return { + useCustomer, + CustomerCard, + } +} +``` + +### Фабрика с зависимостями + +`business/order/order.factory.ts` + +```ts +import type { OrderFactory } from './types/order-factory.type' + +export const orderFactory: OrderFactory = (deps) => { + return { + useOrder, + OrderCard, + } +} +``` + +### Композиция на уровне screen + +```tsx +// screens/home/home.screen.tsx +import { customerFactory } from '@/business/customer' +import { orderFactory } from '@/business/order' + +const customer = customerFactory() +const order = orderFactory({ customer }) + +const { useOrder, OrderCard } = order + +export const HomeScreen = () => { + const currentOrder = useOrder() + + return +} +``` + +## Инфраструктурный модуль + +Инфраструктурный модуль строится вокруг технического сервиса или интеграции. Его структура определяется природой сервиса — фиксированного корневого файла нет. + +Архитектурное описание: [Архитектура → Типы модулей → Инфраструктурный модуль](/docs/basics/architecture/modules#инфраструктурный-модуль). + +Пример модуля темы: ```text -business/auth/ +theme/ +├── index.ts +├── config/ ├── hooks/ -│ └── use-auth.hook.ts -├── services/ -│ └── auth.service.ts -├── stores/ -│ └── auth.store.ts +├── styles/ +└── ui/ +``` + +Пример модуля API-клиента: + +```text +backend-api/ +├── backend-api.client.ts +├── config/ ├── types/ -│ └── auth.type.ts └── index.ts ``` diff --git a/docs/docs/applied/page-level.md b/docs/docs/applied/page-level.md index ee8137b..0c3e7d1 100644 --- a/docs/docs/applied/page-level.md +++ b/docs/docs/applied/page-level.md @@ -13,7 +13,7 @@ description: Как работать со страницами и другими Файлы роутинга не реализуют интерфейс. Они описывают маршрут: читают параметры, получают данные первого рендера, подготавливают кеш или состояние и передают результат в screen. -Границы слоя описаны в [Архитектура → Слои → App](/docs/basics/architecture/reference/layers#слой-app). +Границы слоя описаны в [Архитектура → Слои → App](/docs/basics/architecture/layers#слой-app). ## Граница ответственности diff --git a/docs/docs/applied/project-structure.md b/docs/docs/applied/project-structure.md index 5abdd1d..75bc6fd 100644 --- a/docs/docs/applied/project-structure.md +++ b/docs/docs/applied/project-structure.md @@ -58,7 +58,7 @@ src/ Точка входа приложения и файловый роутинг Next.js (`layout.tsx`, `page.tsx`, route-сегменты). `app/` подключает готовую инициализацию из нижних слоёв, но не реализует провайдеры, стили, UI-компоненты, хуки, сторы или сервисы. -Подробнее о границах слоя: [Архитектура → Слои → App](/docs/basics/architecture/reference/layers#слой-app). +Подробнее о границах слоя: [Архитектура → Слои → App](/docs/basics/architecture/layers#слой-app). ```text src/app/ diff --git a/docs/docs/applied/templates/templates-create.md b/docs/docs/applied/templates/templates-create.md index 6620ae3..6b5f44e 100644 --- a/docs/docs/applied/templates/templates-create.md +++ b/docs/docs/applied/templates/templates-create.md @@ -22,7 +22,7 @@ keywords: [шаблоны, templates, .templates, syntax, переменные, │ ├── styles/ │ │ └── {{name.kebabCase}}.module.css │ ├── types/ -│ │ └── {{name.kebabCase}}.type.ts +│ │ └── {{name.kebabCase}}-props.type.ts │ ├── {{name.kebabCase}}.tsx │ └── index.ts └── store/ # шаблон Zustand стора @@ -32,6 +32,12 @@ keywords: [шаблоны, templates, .templates, syntax, переменные, └── index.ts ``` +## Обязательный шаблон компонента + +Перед созданием компонентов в проекте должен существовать шаблон `.templates/component`. + +Если шаблона нет, компонент не создаётся вручную. Сначала создаётся шаблон компонента, затем компонент генерируется через [VS Code или CLI](/docs/applied/templates/templates-usage). + ## Синтаксис шаблонов ### Переменные diff --git a/docs/docs/applied/templates/templates-usage.md b/docs/docs/applied/templates/templates-usage.md index 1d2e2d8..80ff431 100644 --- a/docs/docs/applied/templates/templates-usage.md +++ b/docs/docs/applied/templates/templates-usage.md @@ -8,6 +8,12 @@ keywords: [шаблоны, templates, generate, VS Code, CLI, gromlab/create, np Генерация файлов из шаблонов через VS Code плагин и CLI. +::: danger Ручное создание запрещено +Файлы, для которых есть шаблоны в `.templates/`, создаются только генератором. Ручное создание компонента, модуля, стора или другого шаблонного блока запрещено. + +Если нужного шаблона нет, сначала создайте шаблон в `.templates/`, затем сгенерируйте код на его основе. +::: + ## Через VS Code Template File Generator | gromlab ([Marketplace](https://marketplace.visualstudio.com/items?itemName=gromlab.vscode-templateFileGenerator), [Open VSX](https://open-vsx.org/extension/gromlab/vscode-templateFileGenerator)) — расширение для генерации файлов и папок из шаблонов через интерфейс редактора. diff --git a/docs/docs/basics/architecture/index.md b/docs/docs/basics/architecture/index.md index 4653ce2..26c587f 100644 --- a/docs/docs/basics/architecture/index.md +++ b/docs/docs/basics/architecture/index.md @@ -1,11 +1,19 @@ ---- -title: SLM Design -description: "Архитектурный подход проекта: что такое SLM и как он устроен." ---- - # SLM Design +Scoped Layered Module Design — модульная архитектура фронтенд-приложений. Код организован по слоям ответственности, а модуль содержит всё, что ему нужно: компоненты, хуки, сторы, типы, стили. -Архитектурный подход проекта: что такое SLM и как он устроен. +::: warning Локальная копия +Документация по архитектуре — локальная копия. Оригинал находится на сайте [slm-design.gromlab.ru](https://slm-design.gromlab.ru/). +::: + +## Разделы спецификации + +Спецификация SLM Design состоит из нескольких связанных разделов. Этот обзор даёт общий контекст, а детальные правила описаны дальше: + +- [Слои](./layers.md) — уровни организации `src/`, направление зависимостей и зона ответственности каждого слоя. +- [Модули](./modules.md) — границы ответственности, публичный API, типы модулей и отличие модуля от компонента. +- [Сегменты](./segments.md) — внутренние папки модуля (`ui/`, `parts/`, `hooks/`, `types/` и другие) и правила размещения файлов. + +Рекомендуемый порядок чтения: обзор → слои → модули → сегменты. ## Преимущества @@ -19,7 +27,7 @@ Cross-domain зависимости в бизнес-слое реализуют ### Разделение ответственности без перегрузки слоёв -Сервисы приложения (`infrastructure/`), UI-кит (`ui/`) и общие ресурсы (`shared/`) — три разных слоя с разной природой. Ни один слой не превращается в свалку разнородного кода. +Сервисы приложения (`infra/`), UI-кит (`ui/`) и общие ресурсы (`shared/`) — три разных слоя с разной природой. Ни один слой не превращается в свалку разнородного кода. ### Горизонтальная инкапсуляция @@ -73,7 +81,7 @@ src/ │ ├── orders/ │ └── chat/ │ -├── infrastructure/ +├── infra/ │ ├── theme/ │ ├── i18n/ │ ├── backend-api/ diff --git a/docs/docs/basics/architecture/reference/layers.md b/docs/docs/basics/architecture/layers.md similarity index 83% rename from docs/docs/basics/architecture/reference/layers.md rename to docs/docs/basics/architecture/layers.md index c9f3407..59f15ee 100644 --- a/docs/docs/basics/architecture/reference/layers.md +++ b/docs/docs/basics/architecture/layers.md @@ -1,11 +1,6 @@ ---- -title: Слои SLM -description: Из каких слоёв состоит SLM-архитектура и как они связаны. ---- +# Слои -# Слои SLM - -Из каких слоёв состоит SLM-архитектура и как они связаны. +Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом. ## Определение @@ -18,7 +13,7 @@ description: Из каких слоёв состоит SLM-архитектур | Группа | Слои | Описание | |--------|------|----------| | Композиция | `app`, `layouts`, `screens`, `widgets` | Собирают интерфейс из готовых блоков: маршруты, каркасы, страницы | -| Ядро | `business`, `infrastructure`, `ui` | Реализация продукта: бизнес-домены, техсервисы, UI-кит | +| Ядро | `business`, `infra`, `ui` | Реализация продукта: бизнес-домены, техсервисы, UI-кит | | Фундамент | `shared` | Общие ресурсы: утилиты, хелперы, стили, конфиги | ## Направление зависимостей @@ -26,12 +21,12 @@ description: Из каких слоёв состоит SLM-архитектур Любой импорт между модулями — только через публичный API. ``` -app → [ layouts | screens ] → widgets → business → infrastructure → ui → shared +app → [ layouts | screens ] → widgets → business → infra → ui → shared ``` - `layouts` и `screens` — параллельные слои, не импортируют друг друга - Модули одного слоя в группе «Композиция» изолированы друг от друга -- Модули одного слоя `infrastructure` и `ui` могут импортировать друг друга через публичный API +- Модули одного слоя `infra` и `ui` могут импортировать друг друга через публичный API - Модули `business` — cross-domain зависимости по коду через фабрику, `import type` напрямую - Импорт типов (`import type`) в «Ядре» разрешён в обоих направлениях @@ -129,7 +124,7 @@ src/widgets/ Бизнес-домены приложения: auth, catalog, orders, checkout, chat. Каждый домен — отдельный модуль со своими типами, логикой, UI и сервисами. -Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`. Cross-domain зависимости по коду реализуются через фабрику. `import type` между доменами разрешён напрямую. +Слой входит в группу «Ядро». Импортирует `infra/`, `ui/`, `shared/`. Каждый бизнес-модуль создаёт публичный runtime API через фабрику в корне. Cross-domain зависимости: runtime — через аргументы фабрики, типы — напрямую через `import type`. Business объединяет то, что в FSD разделено на `features` и `entities`: пользовательские сценарии и бизнес-сущности живут вместе, внутри одного домена. Внутри домена сегменты разделяют ответственность: `types/` — доменная модель, `hooks/` и `services/` — сценарии и логика, `mappers/` — трансформация данных, `parts/` — составные блоки. @@ -160,19 +155,20 @@ src/business/ - Один модуль = один бизнес-домен - Циклические зависимости между доменами запрещены -- Импорт кода между доменами — через фабрику. `import type` — напрямую +- Публичный runtime API — через фабрику в корне модуля (`{name}.factory.ts`). `index.ts` экспортирует только фабрику и type-only экспорты +- Импорт runtime-кода между доменами — через фабрику. `import type` — напрямую - Доменные типы (`User`, `Product`) живут здесь, не в `shared/` -## Слой Infrastructure +## Слой infra Техсервисы приложения: theme, i18n, API-адаптеры, logger, realtime. Каждый сервис — отдельный модуль. -Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`. +Слой входит в группу «Ядро». Импортирует `infra/`, `ui/`, `shared/`. -Отличие от `shared/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги). +Отличие от `shared/`: infra — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги). ```text -src/infrastructure/ +src/infra/ ├── theme/ ├── i18n/ ├── backend-api/ @@ -185,7 +181,7 @@ src/infrastructure/ ### Требования - Один модуль = один техсервис -- Импортирует `infrastructure/`, `ui/`, `shared/` +- Импортирует `infra/`, `ui/`, `shared/` ## Слой UI @@ -236,7 +232,7 @@ src/ui/ Слой входит в группу «Фундамент» — ни о ком не знает, никого не импортирует. -Отличие от `infrastructure/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги). +Отличие от `infra/`: infra — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги). Отличие от `ui/`: UI-компоненты (button, carousel, modal) живут в слое `ui/`, а не здесь. @@ -250,4 +246,4 @@ src/shared/ ### Требования -- Не имеет runtime-состояния +- Не имеет runtime-состояния \ No newline at end of file diff --git a/docs/docs/basics/architecture/modules.md b/docs/docs/basics/architecture/modules.md new file mode 100644 index 0000000..66afe3b --- /dev/null +++ b/docs/docs/basics/architecture/modules.md @@ -0,0 +1,284 @@ +# Модули + +Раздел описывает модуль как границу ответственности в SLM: что считается модулем, что такое компонент внутри модуля и как модуль взаимодействует с остальным кодом. + +## Определение + +**Модуль — минимальная архитектурная единица SLM. Он живёт на одном из слоёв, владеет конкретной областью ответственности и предоставляет наружу только публичный API.** + +Модуль может содержать всё, что нужно этой области: компоненты, вложенные модули, хуки, сторы, сервисы, типы, стили, конфиги и утилиты. Набор сегментов не фиксирован — модуль включает только то, что реально нужно. + +Модуль не обязан быть UI-блоком. Это может быть страница, виджет, бизнес-домен, инфраструктурный сервис или UI-kit сущность. + +Главная граница модуля — не папка, а ответственность. + +## Компонент + +**Компонент — презентационная единица модуля, которая находится только в `ui/` своего родительского модуля и отвечает за отображение части интерфейса.** + +Компонент не является архитектурной единицей: он не владеет сценарием, зависимостями, данными или внутренней структурой. Он работает только внутри границы родительского модуля. + +> Компонент отображает. Модуль организует. + +Компонент не может: + +- Импортировать код проекта за пределами родительского модуля. +- Владеть архитектурными зависимостями. +- Содержать любые компоненты. +- Содержать любые модули. +- Делать внешние запросы. +- Самостоятельно получать данные. +- Выбирать источник данных. +- Композировать данные. +- Вызывать сценарные хуки. +- Оркестрировать сценарий. +- Композировать модули. +- Решать, как устроен процесс. +- Содержать бизнес-логику. +- Содержать сценарную логику. + +Если компоненту требуется что-то из этого списка, он перестаёт быть компонентом и должен быть оформлен как модуль. + +```text +auth/ +├── ui/ +│ └── logout-button/ +│ ├── logout-button.tsx +│ ├── styles/ +│ │ └── logout-button.module.css +│ ├── types/ +│ │ └── logout-button-props.type.ts +│ └── index.ts +└── index.ts +``` + +## Что считается модулем + +Модулем считается папка, которая представляет самостоятельную область ответственности и имеет публичную границу. + +Примеры модулей: + +- `screens/home/` — модуль страницы. +- `widgets/page-heading/` — модуль виджета. +- `business/auth/` — модуль бизнес-домена. +- `infra/theme/` — модуль инфраструктурного сервиса. +- `ui/button/` — модуль UI-kit сущности. +- `screens/home/parts/hero-section/` — вложенный модуль страницы. + +Не считаются модулями: + +- `ui/`, `parts/`, `hooks/`, `types/`, `styles/`, `config/` — это сегменты. +- `screens/shop/`, `business/commerce/` — это группы, если в них нет `index.ts`. +- `screens/home/ui/user-card/` — это компонент, если он находится в `ui/` и соблюдает ограничения компонента. + +## Типы модулей + +Тип модуля определяет обязательный корневой файл и стартовую структуру. + +### UI-модуль + +Модуль строится вокруг основного UI-компонента и обязан иметь основной `.tsx` файл в корне: + +```text +header/ +├── header.tsx +└── index.ts +``` + +`ui/` внутри такого модуля используется только для компонентов, которые помогают корневому `.tsx` файлу. + +### Бизнес-модуль + +Бизнес-модуль — модуль, который строится вокруг публичного runtime API. + +Бизнес-модуль обязан иметь фабрику в корне: + +```text +auth/ +├── auth.factory.ts +├── index.ts +└── types/ +``` + +Фабрика возвращает публичный runtime API модуля. + +### Инфраструктурный модуль + +Инфраструктурный модуль — модуль, который строится вокруг технического сервиса или интеграции. + +Инфраструктурный модуль не обязан иметь фиксированный корневой файл. Его структура определяется природой сервиса. + +```text +theme/ +├── index.ts +├── config/ +├── hooks/ +├── styles/ +└── ui/ +``` + +```text +backend-api/ +├── backend-api.client.ts +├── config/ +├── types/ +└── index.ts +``` + +## Структура + +Модуль состоит из сегментов. Ни один сегмент не обязателен — модуль включает только те части, которые нужны его ответственности. + +```text +{module-name}/ +├── {module-name}.factory.ts # фабрика (для business-модулей) +├── {module-name}.tsx # корневой файл модуля (опционален) +├── ui/ # компоненты модуля +├── parts/ # вложенные модули +├── hooks/ # хуки +├── stores/ # сторы состояния +├── services/ # внешние источники данных +├── mappers/ # трансформация данных между форматами +├── types/ # типы +├── styles/ # стили +├── lib/ # утилиты модуля +├── config/ # константы и конфигурация +└── index.ts # публичный API +``` + +Подробное описание сегментов — в разделе [Сегменты](./segments.md). + +## Публичный API + +Внешний код импортирует модуль только через публичный API. + +```ts +// Хорошо +import { customerFactory } from '@/business/customer' +import type { Customer } from '@/business/customer' +``` + +```ts +// Плохо +import { validateToken } from '@/business/auth/lib/tokens' +``` + +`index.ts` модуля не обязан экспортировать всё содержимое. Он экспортирует только то, что действительно нужно снаружи. + +Внутренние сегменты модуля остаются деталями реализации. + +Business-модуль экспортирует из `index.ts` только фабрику и type-only экспорты. Хуки, компоненты, сервисы, мапперы и утилиты напрямую из `index.ts` не экспортируются — они доступны через API, который возвращает фабрика. + +```ts +// business/customer/index.ts +export { customerFactory } from './customer.factory' + +export type { Customer } from './types/customer.type' +export type { CustomerApi } from './types/customer-api.type' +export type { CustomerDeps } from './types/customer-deps.type' +export type { CustomerFactory } from './types/customer-factory.type' +``` + +## Фабрика + +Business-модуль всегда экспортирует фабрику. Фабрика лежит в корне модуля (`{name}.factory.ts`), типизируется через `{Name}Factory` и возвращает публичный runtime API модуля. + +Всё, что нужно внешнему коду в runtime, должно быть частью API, который возвращает фабрика. + +Модуль без cross-domain зависимостей экспортирует фабрику без аргументов. Модуль с зависимостями — фабрику, принимающую зависимости доменными именами. Типы всегда экспортируются напрямую через `export type` — `import type` не является runtime-зависимостью. + +Компоновка фабрик происходит на уровне модуля-потребителя: screen, layout, widget или любой другой модуль группы «Композиция». + +### Структура business-модуля + +```text +business/customer/ +├── customer.factory.ts +├── index.ts +└── types/ + ├── customer.type.ts + ├── customer-api.type.ts + ├── customer-deps.type.ts + └── customer-factory.type.ts +``` + +### Типы + +```ts +// business/customer/types/customer-api.type.ts +export type CustomerApi = { + useCustomer: () => Customer + CustomerCard: (props: CustomerCardProps) => ReactNode +} +``` + +```ts +// business/order/types/order-deps.type.ts +export type OrderDeps = { + customer: Pick +} +``` + +```ts +// business/order/types/order-factory.type.ts +export type OrderFactory = (deps: OrderDeps) => OrderApi +``` + +### Фабрика без зависимостей + +```ts +// business/customer/customer.factory.ts +import type { CustomerFactory } from './types/customer-factory.type' + +export const customerFactory: CustomerFactory = () => { + return { + useCustomer, + CustomerCard, + } +} +``` + +### Фабрика с зависимостями + +```ts +// business/order/order.factory.ts +import type { OrderFactory } from './types/order-factory.type' + +export const orderFactory: OrderFactory = (deps) => { + return { + useOrder, + OrderCard, + } +} +``` + +### Композиция на уровне screen + +```tsx +// screens/home/home.screen.tsx +import { customerFactory } from '@/business/customer' +import { orderFactory } from '@/business/order' + +const customer = customerFactory() +const order = orderFactory({ customer }) + +const { useOrder, OrderCard } = order + +export const HomeScreen = () => { + const currentOrder = useOrder() + + return +} +``` + +## Жизненный цикл + +Модуль рождается на самом низком уровне использования и поднимается выше только при реальной потребности. + +- Нужен на одной странице → `screens/{name}/parts/` +- Появился в 2+ местах → поднимается по природе: + - абстрактный UI → `ui/` + - блок с данными/логикой → `widgets/` + - представление бизнес-домена → `business/{area}/parts/` + +Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность. \ No newline at end of file diff --git a/docs/docs/basics/architecture/reference/modules.md b/docs/docs/basics/architecture/reference/modules.md deleted file mode 100644 index ee607fb..0000000 --- a/docs/docs/basics/architecture/reference/modules.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -title: Модули SLM -description: Что такое модуль в SLM-архитектуре и как он устроен. ---- - -# Модули SLM - -Что такое модуль в 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 -``` - -## Структура - -Модуль состоит из сегментов. Ни один сегмент не обязателен — модуль может состоять даже из одного `index.ts` с реэкспортом типов. - -```text -{module-name}/ -├── {module-name}.tsx # корневой компонент (опционален) -├── ui/ # компоненты модуля (только .tsx) -├── parts/ # вложенные модули (со своими сегментами) -├── hooks/ # хуки -├── stores/ # сторы состояния -├── services/ # внешние источники данных -├── mappers/ # трансформация данных между форматами -├── types/ # типы -├── styles/ # стили -├── lib/ # утилиты модуля -├── config/ # константы -└── index.ts # публичный API -``` - -Подробное описание каждого сегмента — в разделе [Сегменты](/docs/basics/architecture/reference/segments). - -## Публичный API - -Модуль экспортирует наружу только то, что нужно другим. Всё остальное — внутреннее. - -```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' -``` - -Импорт в обход `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/` - -Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность. diff --git a/docs/docs/basics/architecture/reference/segments.md b/docs/docs/basics/architecture/segments.md similarity index 65% rename from docs/docs/basics/architecture/reference/segments.md rename to docs/docs/basics/architecture/segments.md index 45af86b..7305e67 100644 --- a/docs/docs/basics/architecture/reference/segments.md +++ b/docs/docs/basics/architecture/segments.md @@ -1,11 +1,6 @@ ---- -title: Сегменты SLM -description: Что такое сегмент модуля в SLM-архитектуре и какие они бывают. ---- +# Сегменты -# Сегменты SLM - -Что такое сегмент модуля в SLM-архитектуре и какие они бывают. +Раздел описывает сегменты SLM: что такое сегмент, какие бывают и что в каждом из них лежит. ## Определение @@ -15,7 +10,7 @@ description: Что такое сегмент модуля в SLM-архитек | Сегмент | Содержимое | |---------|------------| -| `ui/` | Вспомогательные компоненты модуля — только `.tsx` файлы | +| `ui/` | Презентационные компоненты родительского модуля | | `parts/` | Вложенные модули со своими сегментами | | `hooks/` | React-хуки | | `stores/` | Сторы состояния | @@ -28,22 +23,44 @@ description: Что такое сегмент модуля в SLM-архитек ## Сегмент ui/ -Вспомогательные компоненты модуля. Содержит только `.tsx` файлы — без своих сегментов, стилей, типов, хуков и публичного API. Использует сегменты родительского модуля. +Презентационные компоненты родительского модуля. `ui/` содержит только компоненты, которые отвечают за отображение части интерфейса и не выходят за границы своего модуля. -Корневой компонент модуля в `ui/` не размещается. Он лежит в корне модуля: `{module-name}.tsx`. +Компонент в `ui/`: + +- Находится в собственной папке. +- Может содержать только `{name}.tsx`, `index.ts`, `styles/`, `types/`. +- Не содержит любые компоненты. +- Не содержит любые модули. +- Не импортирует код проекта за пределами родительского модуля. +- Не делает внешние запросы. +- Не вызывает сценарные хуки. +- Не получает данные самостоятельно, не выбирает источник данных и не композирует данные. +- Не содержит бизнес-логику или сценарную логику. + +Если UI-сущности нужно что-то за пределами этих ограничений, она должна быть оформлена как модуль. Полная граница описана в разделе [Компонент](./modules.md#компонент). + +Корневой файл модуля в `ui/` не размещается. Он лежит в корне модуля: `{module-name}.tsx`. ```text user/ ├── ui/ -│ ├── user-avatar.tsx -│ └── user-status.tsx +│ ├── user-avatar/ +│ │ ├── user-avatar.tsx +│ │ ├── styles/ +│ │ │ └── user-avatar.module.css +│ │ ├── types/ +│ │ │ └── user-avatar-props.type.ts +│ │ └── index.ts +│ └── user-status/ +│ ├── user-status.tsx +│ └── index.ts ├── types/ ├── hooks/ ├── user.tsx └── index.ts ``` -Если компоненту нужны собственные сегменты — это уже не `ui/`, а `parts/`. +Если UI-сущности нужна внутренняя декомпозиция, сценарная логика, получение данных или собственные архитектурные зависимости — это уже не компонент в `ui/`, а модуль в `parts/`. ## Сегмент parts/ @@ -68,7 +85,7 @@ home/ └── index.ts ``` -Отличие от `ui/`: элемент `parts/` — модульная папка со своими сегментами. Элемент `ui/` — вспомогательный компонент, один `.tsx` файл. +Отличие от `ui/`: элемент `parts/` — модульная папка со своими сегментами. Элемент `ui/` — компонент родительского модуля без собственной архитектурной ответственности. Вложенность `parts/` инкапсулирует область разработки горизонтально: каждый разработчик работает в своём `parts/`-модуле, не затрагивая чужие. Это снижает конфликты при параллельной разработке. @@ -156,4 +173,4 @@ lib/ config/ ├── routes.ts └── constants.ts -``` +``` \ No newline at end of file diff --git a/docs/docs/basics/typing.md b/docs/docs/basics/typing.md index c14743d..840765f 100644 --- a/docs/docs/basics/typing.md +++ b/docs/docs/basics/typing.md @@ -9,11 +9,16 @@ description: Как типизируется код в проекте. ## Общие правила -- Указывать типы для параметров компонентов, возвращаемых значений и параметров функций. +- Указывать типы для параметров компонентов и параметров функций. - Предпочитать `type` для описания сущностей и `interface` для расширяемых контрактов. - Избегать `any` и `unknown` без необходимости. - Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины. +## React-компоненты + +- Пропсы компонента типизировать через отдельный `Props`. +- Возвращаемый тип компонента не указывать: TypeScript корректно выводит JSX-результат, а явный `ReactElement` сужает допустимые варианты возврата. + ## Функции - Для публичных функций указывать возвращаемый тип.