docs: обновить архитектуру SLM compositions
- обновлена модель слоёв на app → compositions → business → infra → ui → shared - добавлены правила composition modules и providers-сегмента - обновлены правила монорепозитория для слоя compositions - переписаны React-примеры под page-level композицию - добавлен пример вариантов структуры compositions
This commit is contained in:
@@ -4,6 +4,7 @@ description: Назначение архитектуры, ключевые пр
|
||||
---
|
||||
|
||||
# SLM Design
|
||||
|
||||
Scoped Layered Module Design — модульная архитектура фронтенд-приложений. Код организован по слоям ответственности, а модуль содержит всё, что ему нужно: компоненты, хуки, сторы, типы, стили.
|
||||
|
||||
## Разделы спецификации
|
||||
@@ -13,18 +14,16 @@ Scoped Layered Module Design — модульная архитектура фр
|
||||
- [Слои](/architecture/layers) — уровни организации `src/`, направление зависимостей и зона ответственности каждого слоя.
|
||||
- [Модули](/architecture/modules) — границы ответственности, публичный API, типы модулей и отличие модуля от компонента.
|
||||
- [Сегменты](/architecture/segments) — внутренние папки модуля (`ui/`, `parts/`, `hooks/`, `types/` и другие) и правила размещения файлов.
|
||||
- [Монорепозитории](/architecture/monorepo) — применение SLM в `apps/` и `packages/`, правила выноса общих слоёв и ограничения для business.
|
||||
- [Монорепозитории](/architecture/monorepo) — применение SLM в `apps/` и `packages/`, правила выноса общих слоёв и ограничения для business/compositions.
|
||||
|
||||
Рекомендуемый порядок чтения: обзор → слои → модули → сегменты → монорепозитории.
|
||||
|
||||
## AI Skill
|
||||
|
||||
Готовый self-contained skill для Claude Code и opencode можно скачать как архив: [slm-design.skill.zip](/slm-design/skill/slm-design.skill.zip).
|
||||
|
||||
Архив можно распаковать в корень другого проекта. Он добавит рабочие файлы `.claude/skills/slm-design/SKILL.md` и `.opencode/skills/slm-design/SKILL.md`.
|
||||
|
||||
## Преимущества
|
||||
|
||||
### Единый слой композиции
|
||||
|
||||
Страницы, маршруты и крупные продуктовые части интерфейса собираются в `compositions`. Слой не навязывает жёсткую структуру: команда может использовать `pages/layouts/screens/widgets` или другую организацию под свой фреймворк и продукт.
|
||||
|
||||
### Вертикальная организация домена
|
||||
|
||||
Бизнес-домен не разбивается по техническим слоям — сценарии, сущности, типы и UI живут в одном модуле. Это сокращает время навигации и упрощает сопровождение: все изменения домена локализованы.
|
||||
@@ -35,27 +34,27 @@ Cross-domain зависимости в бизнес-слое реализуют
|
||||
|
||||
### Разделение ответственности без перегрузки слоёв
|
||||
|
||||
Сервисы приложения (`infra/`), UI-кит (`ui/`) и общие ресурсы (`shared/`) — три разных слоя с разной природой. Ни один слой не превращается в свалку разнородного кода.
|
||||
Композиция приложения (`compositions/`), сервисы приложения (`infra/`), UI-кит (`ui/`) и общие ресурсы (`shared/`) — разные слои с разной природой. Ни один слой не превращается в свалку разнородного кода.
|
||||
|
||||
### Графовая композиция там, где она нужна
|
||||
|
||||
Внутри `compositions` допускается граф импортов через публичный API. Это позволяет page-level store, provider или business composition использовать одновременно в layout, screen и widget, не перенося продуктовый runtime-state в `infra` или `shared`.
|
||||
|
||||
### Горизонтальная инкапсуляция
|
||||
|
||||
Вложенные модули (`parts/`) и направление зависимостей позволяют нескольким разработчикам работать над одной областью приложения параллельно, не затрагивая код друг друга.
|
||||
Вложенные модули (`parts/`) и публичные API позволяют нескольким разработчикам работать над одной областью приложения параллельно, не затрагивая код друг друга.
|
||||
|
||||
### Колокация по умолчанию
|
||||
|
||||
Код начинает жизнь рядом с местом использования и поднимается в общие слои только при реальной потребности. Глобальные слои не засоряются преждевременными абстракциями.
|
||||
|
||||
### Явное разделение каркаса и контента
|
||||
|
||||
Каркас группы маршрутов (`layouts/`) и контент конкретной страницы (`screens/`) — независимые слои с собственной ответственностью.
|
||||
|
||||
### Масштабирование через группировку
|
||||
|
||||
При росте проекта слои не теряют структуру — модули группируются по естественным признакам: бизнес-домены по субдоменам, страницы по разделам, UI-компоненты по уровню абстракции (примитивы и композиции).
|
||||
При росте проекта слои не теряют структуру — модули группируются по естественным признакам: композиции по страницам и маршрутам, бизнес-домены по субдоменам, UI-компоненты по уровню абстракции.
|
||||
|
||||
### Адаптация к монорепозиториям
|
||||
|
||||
SLM применяется внутри каждого приложения, а `packages/*` используются только для общего кода из слоёв `ui`, `infra` и `shared`. Бизнес-домены остаются внутри приложений, чтобы не размывать продуктовые границы.
|
||||
SLM применяется внутри каждого приложения, а `packages/*` используются только для общего кода из слоёв `ui`, `infra` и `shared`. `compositions` и бизнес-домены остаются внутри приложений, чтобы не размывать продуктовые границы.
|
||||
|
||||
## Происхождение
|
||||
|
||||
@@ -72,20 +71,20 @@ SLM Design вырос на основе:
|
||||
src/
|
||||
├── app/
|
||||
│
|
||||
├── layouts/
|
||||
│ ├── main/
|
||||
│ └── dashboard/
|
||||
│
|
||||
├── screens/
|
||||
│ ├── home/
|
||||
│ ├── products/
|
||||
│ ├── product-detail/
|
||||
│ └── about/
|
||||
│
|
||||
├── widgets/
|
||||
│ ├── page-heading/
|
||||
│ ├── hero-section/
|
||||
│ └── promo-banner/
|
||||
├── compositions/
|
||||
│ ├── pages/
|
||||
│ │ ├── home/
|
||||
│ │ ├── profile/
|
||||
│ │ └── product-detail/
|
||||
│ ├── layouts/
|
||||
│ │ ├── main/
|
||||
│ │ └── dashboard/
|
||||
│ ├── screens/
|
||||
│ │ ├── home/
|
||||
│ │ └── profile/
|
||||
│ └── widgets/
|
||||
│ ├── page-heading/
|
||||
│ └── promo-banner/
|
||||
│
|
||||
├── business/
|
||||
│ ├── auth/
|
||||
@@ -114,7 +113,10 @@ src/
|
||||
|
||||
## Принципы
|
||||
|
||||
- **Домен — единое целое.** Всё, что относится к домену, живёт в одном модуле.
|
||||
- **Композиция — отдельный слой.** Страницы, маршруты и крупные продуктовые части интерфейса собираются в `compositions`.
|
||||
- **Структура композиции свободна.** Команда сама выбирает организацию внутри `compositions`; базовая рекомендация — `pages/layouts/screens/widgets`.
|
||||
- **Домен — единое целое.** Всё, что относится к домену, живёт в одном business-модуле.
|
||||
- **Колокация.** Код рождается рядом с местом использования и поднимается только при необходимости.
|
||||
- **Зависимости однонаправлены.** Импорты только сверху вниз, только через публичный API.
|
||||
- **Архитектура — каркас, не клетка.** Правила фиксируют направление зависимостей и структуру модуля, остальное определяет команда.
|
||||
- **Зависимости однонаправлены за пределами compositions.** Глобальная стрелка: `app → compositions → business → infra → ui → shared`.
|
||||
- **Внутри compositions допустим граф.** Composition modules могут импортировать друг друга через public API.
|
||||
- **Архитектура — каркас, не клетка.** Правила фиксируют границы ответственности и public API, а внутреннюю форму композиции определяет команда.
|
||||
|
||||
@@ -9,7 +9,7 @@ description: Иерархия слоёв от app до shared, правила з
|
||||
|
||||
## Определение
|
||||
|
||||
**Слой — уровень организации кода внутри `src/`. Каждый слой отвечает за свою область (каркас страницы, бизнес-логика, UI-кит) и задаёт правила для кода внутри: направление импортов, именование, допустимые связи между модулями.**
|
||||
**Слой — уровень организации кода внутри `src/`. Каждый слой отвечает за свою область и задаёт правила для кода внутри: направление импортов, именование, допустимые связи между модулями.**
|
||||
|
||||
## Группы слоёв
|
||||
|
||||
@@ -17,7 +17,7 @@ description: Иерархия слоёв от app до shared, правила з
|
||||
|
||||
| Группа | Слои | Описание |
|
||||
|--------|------|----------|
|
||||
| Композиция | `app`, `layouts`, `screens`, `widgets` | Собирают интерфейс из готовых блоков: маршруты, каркасы, страницы |
|
||||
| Композиция | `app`, `compositions` | Подключают приложение к фреймворку и собирают страницы, маршруты и крупные продуктовые части интерфейса |
|
||||
| Ядро | `business`, `infra`, `ui` | Реализация продукта: бизнес-домены, техсервисы, UI-кит |
|
||||
| Фундамент | `shared` | Общие ресурсы: утилиты, хелперы, стили, конфиги |
|
||||
|
||||
@@ -25,20 +25,24 @@ description: Иерархия слоёв от app до shared, правила з
|
||||
|
||||
Любой импорт между модулями — только через публичный API.
|
||||
|
||||
```
|
||||
app → [ layouts | screens ] → widgets → business → infra → ui → shared
|
||||
```text
|
||||
app → compositions → business → infra → ui → shared
|
||||
```
|
||||
|
||||
- `layouts` и `screens` — параллельные слои, не импортируют друг друга
|
||||
- Модули одного слоя в группе «Композиция» изолированы друг от друга
|
||||
- Модули одного слоя `infra` и `ui` могут импортировать друг друга через публичный API
|
||||
- Модули `business` — cross-domain зависимости по коду через фабрику, `import type` напрямую
|
||||
- `app` подключает приложение к фреймворку и импортирует готовые модули из нижних слоёв
|
||||
- `compositions` импортирует `business`, `infra`, `ui`, `shared`
|
||||
- `business` импортирует `infra`, `ui`, `shared`
|
||||
- `infra` импортирует `infra`, `ui`, `shared`
|
||||
- `ui` импортирует `ui` и `shared`
|
||||
- `shared` не импортирует другие SLM-слои
|
||||
- `business`, `infra`, `ui`, `shared` не импортируют `compositions`
|
||||
- Внутри `compositions` направление импортов между composition modules не фиксируется, но импорты разрешены только через публичный API
|
||||
- Модули `business` используют runtime-зависимости на другие домены только через фабрику, `import type` — напрямую
|
||||
- Импорт типов (`import type`) в «Ядре» разрешён в обоих направлениях
|
||||
|
||||
|
||||
## Слой App
|
||||
|
||||
Точка входа приложения. Отвечает за запуск, роутинг и композицию маршрутов из layout и screen.
|
||||
Точка входа приложения. Отвечает за запуск, роутинг и подключение composition modules к фреймворку.
|
||||
|
||||
В отличие от остальных слоёв, `app/` не содержит модулей SLM. Здесь живут только инфраструктурные файлы, которые не могут быть никаким другим слоем: файлы фреймворка роутинга, точка запуска и код инициализации.
|
||||
|
||||
@@ -46,90 +50,77 @@ app → [ layouts | screens ] → widgets → business → infra → ui → shar
|
||||
|
||||
- Не содержит модулей SLM — только файлы фреймворка, роутинг, инициализация
|
||||
- Содержит: файлы маршрутов, bootstrap, обработку ошибок верхнего уровня (404, error boundary), подключение глобальных стилей и ассетов
|
||||
- Провайдеры и гарды — только подключает готовые из нижних слоёв, не реализует
|
||||
- Провайдеры, guards, layouts, screens и страницы — только подключает готовые из `compositions` или нижних слоёв, не реализует
|
||||
- Не содержит бизнес-логику, UI-компоненты, хуки, сторы, сервисы
|
||||
- Никем не импортируется
|
||||
|
||||
## Слой Layouts
|
||||
## Слой Compositions
|
||||
|
||||
Каркас страницы: общие элементы, одинаковые для группы маршрутов (header, footer, sidebar).
|
||||
`compositions/` — слой сборки страниц, маршрутов и крупных продуктовых частей интерфейса.
|
||||
|
||||
На этом слое собираются page, layout, screen, widget и другие composition modules. Они связываются между собой и с нижними слоями: `business`, `infra`, `ui`, `shared`.
|
||||
|
||||
SLM не фиксирует жёсткую структуру внутри `compositions`. Команда выбирает организацию под фреймворк, роутинг, CMS и продуктовую задачу.
|
||||
|
||||
Базовая рекомендация:
|
||||
|
||||
```text
|
||||
src/layouts/
|
||||
├── main/
|
||||
├── dashboard/
|
||||
└── auth/
|
||||
src/compositions/
|
||||
├── pages/
|
||||
├── layouts/
|
||||
├── screens/
|
||||
└── widgets/
|
||||
```
|
||||
|
||||
`pages`, `layouts`, `screens` и `widgets` внутри `compositions` не являются отдельными SLM-слоями. Это типы композиционных модулей.
|
||||
|
||||
Composition module может содержать обычные сегменты SLM: `ui/`, `parts/`, `hooks/`, `stores/`, `services/`, `mappers/`, `types/`, `styles/`, `lib/`, `config/`, `providers/`.
|
||||
|
||||
Page-level store, provider, guard или business composition размещаются внутри page composition module, если они нужны всей странице.
|
||||
|
||||
```text
|
||||
compositions/pages/profile/
|
||||
├── profile.page.tsx
|
||||
├── profile-business-composition.ts
|
||||
├── providers/
|
||||
├── hooks/
|
||||
├── stores/
|
||||
├── types/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Layout, screen и widget могут получать данные page composition через публичный API соответствующего composition module.
|
||||
|
||||
```ts
|
||||
import { useProfilePageStore } from '@/compositions/pages/profile'
|
||||
```
|
||||
|
||||
Внутри `compositions` направление импортов между composition modules не фиксируется. Допустим граф, но все импорты идут только через public API.
|
||||
|
||||
```ts
|
||||
// Хорошо
|
||||
import { useProfilePageStore } from '@/compositions/pages/profile'
|
||||
|
||||
// Плохо
|
||||
import { useProfilePageStore } from '@/compositions/pages/profile/hooks/use-profile-page-store.hook'
|
||||
```
|
||||
|
||||
### Требования
|
||||
|
||||
- Содержит только модули
|
||||
- Не содержит бизнес-логику
|
||||
- Контекстно-зависимые блоки принимает через пропсы от `app`, не импортирует напрямую
|
||||
|
||||
## Слой Screens
|
||||
|
||||
Контент конкретной страницы: собирает её из модулей нижних слоёв.
|
||||
|
||||
```text
|
||||
src/screens/
|
||||
├── home/
|
||||
├── products/
|
||||
├── product-detail/
|
||||
├── about/
|
||||
└── contacts/
|
||||
```
|
||||
|
||||
Когда количество страниц затрудняет навигацию — вводится группировка по разделам. Группа — папка для организации, не модуль (без `index.ts`).
|
||||
|
||||
```text
|
||||
src/screens/
|
||||
├── shop/
|
||||
│ ├── home/
|
||||
│ ├── products/
|
||||
│ ├── product-detail/
|
||||
│ └── cart/
|
||||
├── account/
|
||||
│ ├── profile/
|
||||
│ ├── settings/
|
||||
│ └── order-history/
|
||||
└── info/
|
||||
├── about/
|
||||
├── contacts/
|
||||
└── faq/
|
||||
```
|
||||
|
||||
### Требования
|
||||
|
||||
- Содержит только модули
|
||||
- Не содержит бизнес-логику
|
||||
- Локальные одноразовые секции живут внутри screen-модуля, не выносятся в `widgets`/`business`
|
||||
|
||||
## Слой Widgets
|
||||
|
||||
Составной блок интерфейса, который компонует модули ядра, но не принадлежит конкретному бизнес-домену. Widget появляется когда блок используется в нескольких screens или layouts.
|
||||
|
||||
Если блок принадлежит домену — он живёт в `business/{area}/`, даже если переиспользуется. Если блок нужен только в одном месте — это `screens/{name}/parts/` или `layouts/{name}/parts/`, а не widget.
|
||||
|
||||
```text
|
||||
src/widgets/
|
||||
├── page-heading/
|
||||
├── hero-section/
|
||||
├── onboarding-checklist/
|
||||
├── promo-banner/
|
||||
└── error-boundary/
|
||||
```
|
||||
|
||||
### Требования
|
||||
|
||||
- Не принадлежит конкретному бизнес-домену. Если блок доменный — он живёт в `business/`
|
||||
- Используется в нескольких screens или layouts
|
||||
- `compositions` содержит composition modules страниц, маршрутов и крупных продуктовых частей интерфейса
|
||||
- Структура внутри `compositions` выбирается командой
|
||||
- Базовая рекомендация: `pages/`, `layouts/`, `screens/`, `widgets/`
|
||||
- `pages`, `layouts`, `screens`, `widgets` внутри `compositions` не являются отдельными SLM-слоями
|
||||
- Providers, stores, guards и business composition размещаются внутри того composition module, которому они принадлежат
|
||||
- Внутри `compositions` импорты между composition modules разрешены в любую сторону, но только через public API
|
||||
- Deep imports внутрь composition modules запрещены
|
||||
- `business`, `infra`, `ui` и `shared` не импортируют `compositions`
|
||||
|
||||
## Слой Business
|
||||
|
||||
Бизнес-домены приложения: auth, catalog, orders, checkout, chat. Каждый домен — отдельный модуль со своими типами, логикой, UI и сервисами.
|
||||
|
||||
Слой входит в группу «Ядро». Импортирует `infra/`, `ui/`, `shared/`. Каждый бизнес-модуль создаёт публичный runtime API через фабрику в корне. Cross-domain зависимости: runtime — через аргументы фабрики, типы — напрямую через `import type`.
|
||||
Слой входит в группу «Ядро». Импортирует `infra/`, `ui/`, `shared/`. Каждый бизнес-модуль создаёт публичный API фабрики в корне. Cross-domain зависимости: runtime — через аргументы фабрики, типы — напрямую через `import type`.
|
||||
|
||||
Business объединяет то, что в FSD разделено на `features` и `entities`: пользовательские сценарии и бизнес-сущности живут вместе, внутри одного домена. Внутри домена сегменты разделяют ответственность: `types/` — доменная модель, `hooks/` и `services/` — сценарии и логика, `mappers/` — трансформация данных, `parts/` — составные блоки.
|
||||
|
||||
@@ -160,7 +151,7 @@ src/business/
|
||||
|
||||
- Один модуль = один бизнес-домен
|
||||
- Циклические зависимости между доменами запрещены
|
||||
- Публичный runtime API — через фабрику в корне модуля (`{name}.factory.ts`). `index.ts` экспортирует только фабрику и type-only экспорты
|
||||
- Публичный API фабрики — через фабрику в корне модуля (`{name}.factory.ts`). `index.ts` экспортирует только фабрику и type-only экспорты
|
||||
- Импорт runtime-кода между доменами — через фабрику. `import type` — напрямую
|
||||
- Доменные типы (`User`, `Product`) живут здесь, не в `shared/`
|
||||
|
||||
@@ -187,6 +178,7 @@ src/infra/
|
||||
|
||||
- Один модуль = один техсервис
|
||||
- Импортирует `infra/`, `ui/`, `shared/`
|
||||
- Не содержит продуктовые composition modules конкретных страниц или маршрутов
|
||||
|
||||
## Слой UI
|
||||
|
||||
@@ -252,3 +244,4 @@ src/shared/
|
||||
### Требования
|
||||
|
||||
- Не имеет runtime-состояния
|
||||
- Не знает о продуктовых composition modules
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Модули
|
||||
description: Структура модуля, типы (UI, бизнес, инфра), публичный API, отличие модуля от компонента
|
||||
description: Структура модуля, типы (композиционный, UI, бизнес, инфра), публичный API, отличие модуля от компонента
|
||||
---
|
||||
|
||||
# Модули
|
||||
@@ -13,7 +13,7 @@ description: Структура модуля, типы (UI, бизнес, инф
|
||||
|
||||
Модуль может содержать всё, что нужно этой области: компоненты, вложенные модули, хуки, сторы, сервисы, типы, стили, конфиги и утилиты. Набор сегментов не фиксирован — модуль включает только то, что реально нужно.
|
||||
|
||||
Модуль не обязан быть UI-блоком. Это может быть страница, виджет, бизнес-домен, инфраструктурный сервис или UI-kit сущность.
|
||||
Модуль не обязан быть UI-блоком. Это может быть page composition, layout composition, screen composition, widget composition, бизнес-домен, инфраструктурный сервис или UI-kit сущность.
|
||||
|
||||
Главная граница модуля — не папка, а ответственность.
|
||||
|
||||
@@ -63,30 +63,62 @@ auth/
|
||||
|
||||
Примеры модулей:
|
||||
|
||||
- `screens/home/` — модуль страницы.
|
||||
- `widgets/page-heading/` — модуль виджета.
|
||||
- `compositions/pages/home/` — модуль page composition.
|
||||
- `compositions/layouts/main/` — модуль layout composition.
|
||||
- `compositions/screens/profile/` — модуль screen composition.
|
||||
- `compositions/widgets/page-heading/` — модуль widget composition.
|
||||
- `business/auth/` — модуль бизнес-домена.
|
||||
- `infra/theme/` — модуль инфраструктурного сервиса.
|
||||
- `ui/button/` — модуль UI-kit сущности.
|
||||
- `screens/home/parts/hero-section/` — вложенный модуль страницы.
|
||||
- `compositions/pages/home/parts/hero-section/` — вложенный модуль page composition.
|
||||
|
||||
Не считаются модулями:
|
||||
|
||||
- `ui/`, `parts/`, `hooks/`, `types/`, `styles/`, `config/` — это сегменты.
|
||||
- `screens/shop/`, `business/commerce/` — это группы, если в них нет `index.ts`.
|
||||
- `screens/home/ui/user-card/` — это компонент, если он находится в `ui/` и соблюдает ограничения компонента.
|
||||
- `ui/`, `parts/`, `hooks/`, `types/`, `styles/`, `config/`, `providers/` — это сегменты.
|
||||
- `compositions/pages/`, `business/commerce/` — это группы, если в них нет `index.ts`.
|
||||
- `compositions/pages/home/ui/user-card/` — это компонент, если он находится в `ui/` и соблюдает ограничения компонента.
|
||||
|
||||
## Типы модулей
|
||||
|
||||
Тип модуля определяет обязательный корневой файл и стартовую структуру.
|
||||
|
||||
### Композиционный модуль
|
||||
|
||||
Композиционный модуль — модуль внутри `compositions`, который участвует в сборке страниц, маршрутов и крупных продуктовых частей интерфейса.
|
||||
|
||||
Он может быть page, layout, screen, widget, block, entry-point, CMS-entry, route segment или другим типом композиции, выбранным командой.
|
||||
|
||||
```text
|
||||
compositions/pages/profile/
|
||||
├── profile.page.tsx
|
||||
├── profile-business-composition.ts
|
||||
├── providers/
|
||||
├── hooks/
|
||||
├── stores/
|
||||
├── parts/
|
||||
├── types/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Композиционный модуль может импортировать другие composition modules через public API. Это отличие слоя `compositions`: внутри него допускается графовая композиция.
|
||||
|
||||
При этом deep imports запрещены.
|
||||
|
||||
```ts
|
||||
// Хорошо
|
||||
import { useProfilePageStore } from '@/compositions/pages/profile'
|
||||
|
||||
// Плохо
|
||||
import { useProfilePageStore } from '@/compositions/pages/profile/hooks/use-profile-page-store.hook'
|
||||
```
|
||||
|
||||
### UI-модуль
|
||||
|
||||
Модуль строится вокруг основного UI-компонента и обязан иметь основной `.tsx` файл в корне:
|
||||
|
||||
```text
|
||||
header/
|
||||
├── header.tsx
|
||||
button/
|
||||
├── button.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
@@ -94,7 +126,7 @@ header/
|
||||
|
||||
### Бизнес-модуль
|
||||
|
||||
Бизнес-модуль — модуль, который строится вокруг публичного runtime API.
|
||||
Бизнес-модуль — модуль, который строится вокруг публичного API фабрики.
|
||||
|
||||
Бизнес-модуль обязан иметь фабрику в корне:
|
||||
|
||||
@@ -105,7 +137,7 @@ auth/
|
||||
└── types/
|
||||
```
|
||||
|
||||
Фабрика возвращает публичный runtime API модуля.
|
||||
Фабрика возвращает публичный API модуля для использования в runtime.
|
||||
|
||||
### Инфраструктурный модуль
|
||||
|
||||
@@ -140,6 +172,7 @@ backend-api/
|
||||
├── {module-name}.tsx # корневой файл модуля (опционален)
|
||||
├── ui/ # компоненты модуля
|
||||
├── parts/ # вложенные модули
|
||||
├── providers/ # провайдеры модуля
|
||||
├── hooks/ # хуки
|
||||
├── stores/ # сторы состояния
|
||||
├── services/ # внешние источники данных
|
||||
@@ -184,32 +217,49 @@ export type { CustomerDeps } from './types/customer-deps.type'
|
||||
export type { CustomerFactory } from './types/customer-factory.type'
|
||||
```
|
||||
|
||||
Composition module экспортирует через `index.ts` только безопасный контракт, который нужен другим composition modules или `app`: page/layout/screen/widget, provider, hooks доступа, типы. Внутренние stores, context objects и функции создания состояния не экспортируются без необходимости.
|
||||
|
||||
Если layout, screen или widget импортируют hooks из page composition, не смешивайте в одном public API готовую page composition и hooks для дочерних модулей: это может создать runtime-цикл.
|
||||
|
||||
```ts
|
||||
// compositions/pages/profile/index.ts
|
||||
export { ProfilePageProvider } from './providers/profile-page.provider'
|
||||
export { useProfilePageStore } from './hooks/use-profile-page-store.hook'
|
||||
export { useProfileBusinessComposition } from './hooks/use-profile-business-composition.hook'
|
||||
|
||||
export type { ProfilePageState } from './types/profile-page-state.type'
|
||||
```
|
||||
|
||||
## Фабрика
|
||||
|
||||
Business-модуль всегда экспортирует фабрику. Фабрика лежит в корне модуля (`{name}.factory.ts`), типизируется через `{Name}Factory` и возвращает публичный runtime API модуля.
|
||||
Business-модуль всегда экспортирует фабрику. Фабрика лежит в корне модуля (`{name}.factory.ts`), типизируется через `{Name}Factory` и возвращает публичный API фабрики.
|
||||
|
||||
Всё, что нужно внешнему коду в runtime, должно быть частью API, который возвращает фабрика.
|
||||
|
||||
Модуль без cross-domain зависимостей экспортирует фабрику без аргументов. Модуль с зависимостями — фабрику, принимающую зависимости доменными именами. Типы всегда экспортируются напрямую через `export type` — `import type` не является runtime-зависимостью.
|
||||
|
||||
Компоновка фабрик происходит на уровне модуля-потребителя: screen, layout, widget или любой другой модуль группы «Композиция».
|
||||
Компоновка фабрик происходит в модуле-потребителе на слое `compositions`.
|
||||
|
||||
### Примеры
|
||||
|
||||
Пример реализации фабрики в React см. в [Создание фабрики](/examples/react/factory).
|
||||
|
||||
Пример композиции фабрик в React screen-модуле см. в [Композиция фабрик](/examples/react/factory-composition).
|
||||
Пример композиции фабрик в React composition module см. в [Композиция фабрик](/examples/react/factory-composition).
|
||||
|
||||
Пример композиции фабрик через React Provider см. в [Композиция через Provider](/examples/react/composition-provider).
|
||||
Пример page-level Provider в React см. в [Композиция через Provider](/examples/react/composition-provider).
|
||||
|
||||
Примеры разных структур слоя `compositions` см. в [Структуры compositions](/examples/react/composition-structures).
|
||||
|
||||
## Жизненный цикл
|
||||
|
||||
Модуль рождается на самом низком уровне использования и поднимается выше только при реальной потребности.
|
||||
|
||||
- Нужен на одной странице → `screens/{name}/parts/`
|
||||
- Появился в 2+ местах → поднимается по природе:
|
||||
- абстрактный UI → `ui/`
|
||||
- блок с данными/логикой → `widgets/`
|
||||
- представление бизнес-домена → `business/{area}/parts/`
|
||||
- Нужен одной странице, route branch или крупной продуктовой части интерфейса → внутри соответствующего composition module.
|
||||
- Нужен нескольким частям одной страницы → внутри page composition или другого общего composition scope.
|
||||
- Нужен нескольким страницам или маршрутам → отдельный composition module внутри `compositions`.
|
||||
- Абстрактный UI без бизнес-логики → `ui/`.
|
||||
- Представление или сценарий бизнес-домена → `business/{domain}/`.
|
||||
- Технический сервис → `infra/`.
|
||||
- Общая чистая утилита → `shared/`.
|
||||
|
||||
Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность.
|
||||
|
||||
@@ -21,9 +21,7 @@ repo/
|
||||
│ ├── web/
|
||||
│ │ └── src/
|
||||
│ │ ├── app/
|
||||
│ │ ├── layouts/
|
||||
│ │ ├── screens/
|
||||
│ │ ├── widgets/
|
||||
│ │ ├── compositions/
|
||||
│ │ ├── business/
|
||||
│ │ ├── infra/
|
||||
│ │ ├── ui/
|
||||
@@ -78,17 +76,17 @@ packages/shared/
|
||||
|
||||
## Что остаётся в приложении
|
||||
|
||||
Слои `app`, `layouts`, `screens`, `widgets` и `business` остаются внутри конкретного приложения.
|
||||
Слои `app`, `compositions` и `business` остаются внутри конкретного приложения.
|
||||
|
||||
```text
|
||||
apps/web/src/app/
|
||||
apps/web/src/layouts/
|
||||
apps/web/src/screens/
|
||||
apps/web/src/widgets/
|
||||
apps/web/src/compositions/
|
||||
apps/web/src/business/
|
||||
```
|
||||
|
||||
`app`, `layouts` и `screens` привязаны к роутингу, каркасу и страницам конкретного приложения. `widgets` не выносятся в пакеты, потому что это слой композиции интерфейса приложения.
|
||||
`app` привязан к фреймворку и entry points приложения.
|
||||
|
||||
`compositions` привязан к страницам, маршрутам и крупным продуктовым частям интерфейса конкретного приложения. Этот слой не выносится в `packages/*`, потому что отражает продуктовую сборку приложения.
|
||||
|
||||
`business` не выносится в `packages/*`. Домены остаются рядом со сценариями приложения, чтобы не превращать монорепозиторий в общий бизнес-слой.
|
||||
|
||||
@@ -193,7 +191,7 @@ packages -/→ apps
|
||||
Внутри приложения продолжает действовать обычное направление зависимостей SLM.
|
||||
|
||||
```text
|
||||
app → [ layouts | screens ] → widgets → business → infra → ui → shared
|
||||
app → compositions → business → infra → ui → shared
|
||||
```
|
||||
|
||||
Пакеты не должны нарушать природу своей группы: `packages/ui/*` не импортирует `packages/infra/*`, `packages/shared` не импортирует другие группы, а `packages/infra/*` не знает о приложениях.
|
||||
@@ -206,11 +204,11 @@ app → [ layouts | screens ] → widgets → business → infra → ui → shar
|
||||
|
||||
```text
|
||||
# Плохо
|
||||
apps/web/src/screens/home/parts/promo-section/
|
||||
apps/web/src/compositions/pages/home/parts/promo-section/
|
||||
packages/ui/promo-section/
|
||||
```
|
||||
|
||||
Если блок нужен только одной странице или отражает продуктовую композицию конкретного приложения, он остаётся локальным `parts/`-модулем.
|
||||
Если блок нужен только одной странице или отражает продуктовую композицию конкретного приложения, он остаётся локальным composition module.
|
||||
|
||||
## Конфигурационные пакеты
|
||||
|
||||
@@ -227,7 +225,7 @@ packages/ui/promo-section/
|
||||
- В `packages/infra/*` размещаются пакеты самостоятельных инфраструктурных модулей.
|
||||
- `packages/shared` является единым пакетом для переиспользуемых утилит и helpers.
|
||||
- Модуль можно размещать в пакете, если он потенциально будет использоваться в двух и более фронтенд-приложениях.
|
||||
- `business`, `app`, `layouts`, `screens`, `widgets` не выносятся в пакеты.
|
||||
- `app`, `compositions` и `business` не выносятся в пакеты.
|
||||
- Проектные стили, типы приложения и продуктовые конфиги не выносятся в `packages/shared`.
|
||||
- Пакеты не импортируют приложения.
|
||||
- Межпакетные импорты идут только через публичный API.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Сегменты
|
||||
description: Сегменты внутри модуля (ui/, model/, lib/ и др.), назначение и правила размещения файлов
|
||||
description: Сегменты внутри модуля (ui/, parts/, hooks/ и др.), назначение и правила размещения файлов
|
||||
---
|
||||
|
||||
# Сегменты
|
||||
@@ -17,6 +17,7 @@ description: Сегменты внутри модуля (ui/, model/, lib/ и д
|
||||
|---------|------------|
|
||||
| `ui/` | Презентационные компоненты родительского модуля |
|
||||
| `parts/` | Вложенные модули со своими сегментами |
|
||||
| `providers/` | Провайдеры модуля |
|
||||
| `hooks/` | React-хуки |
|
||||
| `stores/` | Сторы состояния |
|
||||
| `services/` | Работа с внешними источниками данных |
|
||||
@@ -26,6 +27,8 @@ description: Сегменты внутри модуля (ui/, model/, lib/ и д
|
||||
| `lib/` | Утилиты и хелперы модуля |
|
||||
| `config/` | Константы и конфигурация |
|
||||
|
||||
Сегменты не являются обязательными. Например, `providers/` нужен только модулю, который владеет провайдерами. Если provider, store или guard относится к конкретной странице или маршруту, он размещается внутри соответствующего composition module, а не в `infra` или `shared`.
|
||||
|
||||
## Сегмент ui/
|
||||
|
||||
Презентационные компоненты родительского модуля. `ui/` содержит только компоненты, которые отвечают за отображение части интерфейса и не выходят за границы своего модуля.
|
||||
@@ -72,7 +75,7 @@ user/
|
||||
Вложенные модули со своими сегментами. `parts/` содержит только модули: каждый элемент `parts/` — папка полноценного модуля с собственным публичным API. Отдельные `.tsx`, стили, хуки или произвольные файлы в `parts/` не размещаются.
|
||||
|
||||
```text
|
||||
home/
|
||||
compositions/pages/home/
|
||||
├── parts/
|
||||
│ ├── hero-section/
|
||||
│ │ ├── hero-section.tsx
|
||||
@@ -86,7 +89,7 @@ home/
|
||||
│ ├── features-section.tsx
|
||||
│ ├── hooks/
|
||||
│ └── index.ts
|
||||
├── home.screen.tsx
|
||||
├── home.page.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
@@ -96,6 +99,18 @@ home/
|
||||
|
||||
Если вложенный модуль обрастает своими `parts/` — это сигнал, что он достаточно самостоятельный для подъёма на уровень выше.
|
||||
|
||||
## Сегмент providers/
|
||||
|
||||
Провайдеры модуля: React Context providers, провайдеры scope-состояния, провайдеры композиции фабрик или другие обёртки, которые принадлежат модулю.
|
||||
|
||||
```text
|
||||
providers/
|
||||
├── profile-page.provider.tsx
|
||||
└── profile-business-composition.provider.tsx
|
||||
```
|
||||
|
||||
Provider размещается в том модуле, который владеет соответствующим состоянием или композицией. Page-level provider живёт в page composition module; application-level provider, завязанный на фреймворк, подключается в `app`, но реализуется в нижнем подходящем слое.
|
||||
|
||||
## Сегмент hooks/
|
||||
|
||||
React-хуки модуля. Инкапсулируют логику, состояние, подписки, побочные эффекты.
|
||||
@@ -117,6 +132,8 @@ stores/
|
||||
└── session.store.ts
|
||||
```
|
||||
|
||||
Стор размещается в модуле-владельце. Если состояние нужно всей странице, оно живёт в page composition module. Если состояние относится к бизнес-домену, оно живёт в business-модуле.
|
||||
|
||||
## Сегмент services/
|
||||
|
||||
Работа с внешними источниками данных: API-вызовы, запросы, подписки.
|
||||
|
||||
Reference in New Issue
Block a user