docs: обновить архитектуру SLM compositions
All checks were successful
CI/CD Pipeline / build (push) Successful in 44s
CI/CD Pipeline / docker (push) Successful in 1m17s
CI/CD Pipeline / deploy (push) Successful in 8s

- обновлена модель слоёв на app → compositions → business → infra → ui → shared
- добавлены правила composition modules и providers-сегмента
- обновлены правила монорепозитория для слоя compositions
- переписаны React-примеры под page-level композицию
- добавлен пример вариантов структуры compositions
This commit is contained in:
2026-05-26 23:46:11 +03:00
parent 9a962f37b5
commit 89cc873c19
10 changed files with 503 additions and 329 deletions

View File

@@ -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,19 +71,19 @@ SLM Design вырос на основе:
src/
├── app/
├── layouts/
│ ├── main/
└── dashboard/
├── screens/
│ ├── home/
│ ├── products/
├── product-detail/
── about/
├── widgets/
├── compositions/
│ ├── pages/
│ ├── home/
│ ├── profile/
│ │ └── product-detail/
│ ├── layouts/
│ ├── main/
│ └── dashboard/
── screens/
│ ├── home/
│ │ └── profile/
│ └── widgets/
│ ├── page-heading/
│ ├── hero-section/
│ └── promo-banner/
├── business/
@@ -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, а внутреннюю форму композиции определяет команда.

View File

@@ -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

View File

@@ -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/`.
Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность.

View File

@@ -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.

View File

@@ -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-вызовы, запросы, подписки.

View File

@@ -1,249 +1,286 @@
---
title: Композиция через Provider
description: Пример композиции бизнес-фабрик screen-модуля через React Provider
description: Пример page-level Provider для composition modules в React-проекте
---
# Композиция через Provider
Раздел показывает, как screen-модуль может получить готовую композицию бизнес-доменов через React Context, не вызывая фабрики внутри себя.
Раздел показывает, как page composition может владеть provider, store и business composition, которые нужны layout, screen и другим composition modules.
## Идея
Screen получает готовый API бизнес-доменов через React Context. Граф фабрик собирается снаружи, например в роутере, а внутренние `parts/` достают нужные домены через хук без пропс-дриллинга.
Page composition хранит состояние и композицию бизнес-доменов на уровне страницы. Layout и screen не импортируют друг друга: они получают доступ к page-level данным через публичный API page composition.
В примере page composition владеет scope-контрактом страницы, но не экспортирует готовый `ProfilePage`, потому что layout и screen импортируют hooks из `pages/profile`. Дерево страницы собирается в `app` или в отдельном entry-point composition module.
## Принципы
1. **Принадлежность.** Provider, Context и хук принадлежат конкретному screen-модулю и лежат в его сегментах.
2. **Внутренний тип.** Тип композиции не экспортируется наружу — это закрывает доступ из бизнес-модулей.
3. **Внутренний хук.** Хук доступа не экспортируется — доступен только внутри screen и его `parts/`.
4. **Публичный Provider.** Только Provider экспортируется через `index.ts`, чтобы роутер мог обернуть screen.
5. **Сборка снаружи.** Граф фабрик собирается в роутере или другом композиторе, screen фабрики не вызывает.
6. **Запрет для бизнеса.** Бизнес-модули не используют провайдеры композиции. Cross-domain зависимости передаются только через аргументы фабрики.
1. **Владение.** Page-level store, provider и business composition принадлежат page composition module.
2. **Обычные сегменты.** Provider, hooks, stores и types лежат в обычных сегментах модуля: `providers/`, `hooks/`, `stores/`, `types/`.
3. **Публичный контракт.** Page composition экспортирует только безопасные hooks, provider и типы, которые нужны другим composition modules или `app`.
4. **Сборка снаружи business.** Business-модули не используют page-level providers. Cross-domain зависимости передаются только через аргументы фабрики.
5. **Без deep imports.** Layout и screen импортируют hooks только из public API page composition.
## Структура модуля
## Структура модулей
```text
screens/main/
├── main.screen.tsx
compositions/pages/profile/
├── profile-business-composition.ts
├── providers/
│ └── main-composition.provider.tsx
│ └── profile-page.provider.tsx
├── hooks/
── use-main-composition.hook.ts
── use-profile-page-store.hook.ts
│ └── use-profile-business-composition.hook.ts
├── stores/
│ └── profile-page.store.ts
├── types/
│ └── main-composition.type.ts
── parts/
│ └── featured-products/
│ ├── featured-products.tsx
│ └── index.ts
│ └── profile-page-state.type.ts
── index.ts
compositions/layouts/profile-main/
├── profile-main.layout.tsx
└── index.ts
compositions/screens/profile/
├── profile.screen.tsx
└── index.ts
```
Сегмент `providers/` — расширение стандартного набора SLM. Спецификация это разрешает: команда сама определяет, какие сегменты используются.
## Тип состояния страницы
## Распределение по сегментам
| Файл | Сегмент | Назначение |
|------|---------|------------|
| `main-composition.type.ts` | `types/` | TypeScript-тип композиции |
| `main-composition.provider.tsx` | `providers/` | Context и Provider-компонент |
| `use-main-composition.hook.ts` | `hooks/` | React-хук доступа |
| `main.screen.tsx` | корень | Корневой компонент screen-модуля |
| `featured-products/` | `parts/` | Вложенный модуль со своим публичным API |
## Тип композиции
Файл: `screens/main/types/main-composition.type.ts`.
Тип описывает, какие бизнес-домены доступны на этой странице. Он не экспортируется через `index.ts`, чтобы другие модули не зависели от внутренней формы композиции screen.
Файл: `compositions/pages/profile/types/profile-page-state.type.ts`.
```ts
import type { CatalogApi } from '@/business/catalog'
import type { CartApi } from '@/business/cart'
export type MainComposition = {
catalog: CatalogApi
cart: CartApi
export type ProfilePageState = {
title: string
isSidebarOpen: boolean
setSidebarOpen: (value: boolean) => void
}
```
## Context и Provider
## Store страницы
Файл: `screens/main/providers/main-composition.provider.tsx`.
Файл: `compositions/pages/profile/stores/profile-page.store.ts`.
Context — внутренняя деталь Provider, наружу он не экспортируется. Значение `null` по умолчанию нужно, чтобы хук мог проверить отсутствие Provider в дереве.
```ts
import { createStore } from 'zustand/vanilla'
import type { ProfilePageState } from '../types/profile-page-state.type'
Provider-компонент экспортируется через `index.ts`. Роутер передаёт в `value` уже собранный граф фабрик со стабильной ссылкой.
export const createProfilePageStore = () =>
createStore<ProfilePageState>((set) => ({
title: 'Profile',
isSidebarOpen: false,
setSidebarOpen: (value) => set({ isSidebarOpen: value }),
}))
```
`createProfilePageStore` не экспортируется через public API модуля. Это внутренняя деталь создания состояния.
## Business composition страницы
Файл: `compositions/pages/profile/profile-business-composition.ts`.
```ts
import { authFactory } from '@/business/auth'
import { profileFactory } from '@/business/profile'
export const createProfileBusinessComposition = () => {
const auth = authFactory()
const profile = profileFactory({ auth })
return { auth, profile }
}
```
Business composition собирается на слое `compositions`, а не внутри business-модулей.
## Provider страницы
Файл: `compositions/pages/profile/providers/profile-page.provider.tsx`.
```tsx
import { createContext, type ReactNode } from 'react'
import type { MainComposition } from '../types/main-composition.type'
import { createContext, useRef, type ReactNode } from 'react'
import type { StoreApi } from 'zustand/vanilla'
import { createProfileBusinessComposition } from '../profile-business-composition'
import { createProfilePageStore } from '../stores/profile-page.store'
import type { ProfilePageState } from '../types/profile-page-state.type'
export const MainCompositionContext = createContext<MainComposition | null>(null)
type ProfileBusinessComposition = ReturnType<typeof createProfileBusinessComposition>
type ProfilePageProviderValue = {
store: StoreApi<ProfilePageState>
business: ProfileBusinessComposition
}
export const ProfilePageContext = createContext<ProfilePageProviderValue | null>(null)
type Props = {
value: MainComposition
children: ReactNode
}
export const MainCompositionProvider = ({ value, children }: Props) => (
<MainCompositionContext.Provider value={value}>
export const ProfilePageProvider = ({ children }: Props) => {
const valueRef = useRef<ProfilePageProviderValue | null>(null)
if (!valueRef.current) {
valueRef.current = {
store: createProfilePageStore(),
business: createProfileBusinessComposition(),
}
}
return (
<ProfilePageContext.Provider value={valueRef.current}>
{children}
</MainCompositionContext.Provider>
)
</ProfilePageContext.Provider>
)
}
```
## Хук доступа
Context object остаётся технической деталью provider и не должен использоваться внешними модулями напрямую. Наружу экспортируются hooks доступа.
Файл: `screens/main/hooks/use-main-composition.hook.ts`.
## Hooks доступа
Хук остаётся внутренним и не экспортируется через `index.ts` модуля. Он доступен только внутри screen и его `parts/`.
Если хук используется вне Provider, он бросает ошибку. Это даёт раннюю диагностику неправильной композиции дерева.
Файл: `compositions/pages/profile/hooks/use-profile-page-store.hook.ts`.
```ts
import { useContext } from 'react'
import { MainCompositionContext } from '../providers/main-composition.provider'
import { useStore } from 'zustand'
import { ProfilePageContext } from '../providers/profile-page.provider'
import type { ProfilePageState } from '../types/profile-page-state.type'
export const useProfilePageStore = <T,>(selector: (state: ProfilePageState) => T) => {
const ctx = useContext(ProfilePageContext)
export const useMainComposition = () => {
const ctx = useContext(MainCompositionContext)
if (!ctx) {
throw new Error('useMainComposition must be used within MainCompositionProvider')
throw new Error('useProfilePageStore must be used within ProfilePageProvider')
}
return ctx
return useStore(ctx.store, selector)
}
```
## Сборка графа в роутере
Файл: `compositions/pages/profile/hooks/use-profile-business-composition.hook.ts`.
Файл: `app/router.tsx`.
```ts
import { useContext } from 'react'
import { ProfilePageContext } from '../providers/profile-page.provider'
Роутер или другой композитор собирает граф фабрик в точке использования screen. Каждый домен получает свои зависимости через аргументы фабрики.
export const useProfileBusinessComposition = () => {
const ctx = useContext(ProfilePageContext)
Фабрики вызываются вне React-компонента, если не зависят от runtime-параметров. Так API доменов не пересоздаётся на каждый рендер route-компонента.
if (!ctx) {
throw new Error('useProfileBusinessComposition must be used within ProfilePageProvider')
}
```tsx
import { MainScreen, MainCompositionProvider } from '@/screens/main'
import { catalogFactory } from '@/business/catalog'
import { cartFactory } from '@/business/cart'
import { authFactory } from '@/business/auth'
const auth = authFactory()
const catalog = catalogFactory()
const cart = cartFactory({ auth })
const MainRoute = () => (
<MainCompositionProvider value={{ catalog, cart }}>
<MainScreen />
</MainCompositionProvider>
)
return ctx.business
}
```
## Корневой компонент screen
## Layout использует page-level store
Файл: `screens/main/main.screen.tsx`.
Screen получает нужные домены из композиции и достаёт из API готовые хуки, компоненты или функции. В JSX используются уже локальные `useCategories` и `CategoryList`, а не обращение к фабричному API через точку.
Файл: `compositions/layouts/profile-main/profile-main.layout.tsx`.
```tsx
import { useMainComposition } from './hooks/use-main-composition.hook'
import { FeaturedProducts } from './parts/featured-products'
import type { ReactNode } from 'react'
import { useProfilePageStore } from '@/compositions/pages/profile'
export const MainScreen = () => {
const { catalog } = useMainComposition()
const { useCategories, CategoryList } = catalog
const categories = useCategories()
type Props = {
children: ReactNode
}
export const ProfileMainLayout = ({ children }: Props) => {
const title = useProfilePageStore((state) => state.title)
const isSidebarOpen = useProfilePageStore((state) => state.isSidebarOpen)
return (
<div>
<CategoryList categories={categories} />
<FeaturedProducts />
<div data-sidebar-open={isSidebarOpen}>
<header>{title}</header>
<main>{children}</main>
</div>
)
}
```
## Вложенный part
Layout импортирует hook из public API page composition. Он не импортирует screen и не лезет во внутренние файлы `pages/profile`.
Файл: `screens/main/parts/featured-products/featured-products.tsx`.
## Screen использует business composition
Вложенный модуль получает доступ к той же композиции родительского screen. Промежуточные компоненты не прокидывают домены через props.
Из API доменов достаются готовые сущности: `useFeatured`, `ProductCard` и `addItem`. Компонент работает с ними напрямую.
Файл: `compositions/screens/profile/profile.screen.tsx`.
```tsx
import { useMainComposition } from '../../hooks/use-main-composition.hook'
import { useProfileBusinessComposition } from '@/compositions/pages/profile'
export const FeaturedProducts = () => {
const { catalog, cart } = useMainComposition()
const { useFeatured, ProductCard } = catalog
const { addItem } = cart
const products = useFeatured()
export const ProfileScreen = () => {
const { profile } = useProfileBusinessComposition()
const { useCurrentProfile, ProfileCard } = profile
const currentProfile = useCurrentProfile()
return <ProfileCard profile={currentProfile} />
}
```
Screen получает готовые доменные API из page composition и не собирает граф фабрик самостоятельно.
## Публичный API page composition
Файл: `compositions/pages/profile/index.ts`.
```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'
```
Внутренние `createProfilePageStore`, `createProfileBusinessComposition` и `ProfilePageContext` не экспортируются через public API.
Если нужен готовый `ProfilePage`, его лучше собрать в отдельном entry-point composition module или прямо в роутере. Не смешивайте в одном public API и готовую page composition, и hooks, которые импортируют её дочерние layout/screen modules: это может создать runtime-цикл.
## Подключение в app
В React Router можно собрать дерево прямо в route config:
```tsx
import { ProfilePageProvider } from '@/compositions/pages/profile'
import { ProfileMainLayout } from '@/compositions/layouts/profile-main'
import { ProfileScreen } from '@/compositions/screens/profile'
export const profileRoute = {
path: '/profile',
element: (
<ProfilePageProvider>
<ProfileMainLayout>
<ProfileScreen />
</ProfileMainLayout>
</ProfilePageProvider>
),
}
```
В Next App Router композиция может быть физически разложена по файлам `app`, но реализация остаётся в `compositions`.
```tsx
// app/(profile)/layout.tsx
import { ProfilePageProvider } from '@/compositions/pages/profile'
import { ProfileMainLayout } from '@/compositions/layouts/profile-main'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
{products.map((product) => (
<ProductCard
key={product.id}
product={product}
onAdd={() => addItem(product.id)}
/>
))}
</div>
<ProfilePageProvider>
<ProfileMainLayout>{children}</ProfileMainLayout>
</ProfilePageProvider>
)
}
```
Файл: `screens/main/parts/featured-products/index.ts`.
```tsx
// app/(profile)/page.tsx
import { ProfileScreen } from '@/compositions/screens/profile'
```ts
export { FeaturedProducts } from './featured-products'
export default function Page() {
return <ProfileScreen />
}
```
## Публичный API screen-модуля
Файл: `screens/main/index.ts`.
Наружу экспортируются только screen и его Provider. `MainComposition`, `MainCompositionContext` и `useMainComposition` остаются деталями реализации.
```ts
export { MainScreen } from './main.screen'
export { MainCompositionProvider } from './providers/main-composition.provider'
```
## Почему тип композиции не экспортируется
Внутренний тип закрывает доступ к форме композиции из внешних модулей. Бизнес-модуль не должен знать, какие домены собраны для конкретного screen.
Такой импорт из бизнес-модуля не должен быть возможен через публичный API screen.
```ts
import type { MainComposition } from '@/screens/main'
```
Когда тип остаётся внутренним, такая связь невозможна через публичный API screen-модуля.
## Почему хук не экспортируется
Если хук доступа сделать публичным, любой модуль сможет вызвать его напрямую. Внутренний хук доступен только через относительные импорты внутри screen-модуля и его `parts/`.
## Почему Provider экспортируется
Provider безопасно экспортировать: сам по себе он не даёт доступ к данным, а только принимает готовую композицию и передаёт её детям внутри React-дерева.
## Стабильность value
Фабрики создаются на уровне модуля, поэтому `catalog` и `cart` сохраняют ссылки между рендерами `MainRoute`.
Если домены зависят от runtime-параметров, граф нужно собирать в отдельном композиторе для этих параметров и передавать в Provider уже готовую композицию.
## Расширение на другие screen-модули
Паттерн повторяется для каждого screen, которому нужна композиция бизнес-доменов.
```text
screens/checkout/providers/checkout-composition.provider.tsx
screens/checkout/hooks/use-checkout-composition.hook.ts
screens/checkout/types/checkout-composition.type.ts
```
Имена включают имя screen-модуля. Не используйте универсальные названия вроде `useComposition` или `useScope`: по имени файла должно быть понятно, к какой странице привязан Context.
`app` размещает готовые composition modules по правилам фреймворка, но не реализует их внутри себя.

View File

@@ -0,0 +1,73 @@
---
title: Структуры compositions
description: Примеры организации слоя compositions под разные способы сборки React-приложения
---
# Структуры compositions
Раздел показывает, что SLM не фиксирует жёсткую структуру внутри `compositions`. Команда выбирает организацию под фреймворк, роутинг, CMS и продуктовую задачу.
## Базовая рекомендация
Подходит для большинства приложений, где есть явные страницы, layouts, screens и переиспользуемые композиционные блоки.
```text
src/compositions/
├── pages/
│ ├── home/
│ └── profile/
├── layouts/
│ ├── main/
│ └── dashboard/
├── screens/
│ ├── home/
│ └── profile/
└── widgets/
├── page-heading/
└── promo-banner/
```
`pages`, `layouts`, `screens` и `widgets` здесь не являются отдельными SLM-слоями. Это типы composition modules внутри одного слоя `compositions`.
## Entry-points и blocks
Подходит для проектов, где точка сборки не всегда является страницей: CMS registry, embedded UI, route entries, feature entries.
```text
src/compositions/
├── entry-points/
│ ├── cms-profile/
│ └── embedded-checkout/
├── pages/
│ └── profile/
├── layouts/
│ └── profile-main/
├── screens/
│ └── profile/
└── blocks/
├── profile-summary/
└── recommended-products/
```
## Группировка вокруг продукта
Подходит, когда удобнее держать все части одной крупной области рядом.
```text
src/compositions/
└── profile/
├── page/
├── layout/
├── screen/
└── blocks/
```
## Главное правило
Любая структура допустима, если соблюдаются границы слоя:
- `app` подключает готовые composition modules к фреймворку.
- `compositions` может импортировать `business`, `infra`, `ui`, `shared`.
- `business`, `infra`, `ui`, `shared` не импортируют `compositions`.
- Импорты между composition modules идут только через public API.
- Deep imports внутрь composition modules запрещены.

View File

@@ -1,27 +1,29 @@
---
title: Композиция фабрик
description: Пример композиции business-фабрик на уровне screen-модуля в React-проекте
description: Пример композиции business-фабрик на уровне composition module в React-проекте
---
# Композиция фабрик
Раздел показывает, как собрать API нескольких business-модулей в React screen-модуле. Пример подходит для простой композиции, когда screen сам является точкой использования доменов.
Раздел показывает, как собрать API нескольких business-модулей в React composition module. Пример подходит для простой композиции, когда page composition сама является точкой использования доменов.
## Идея
Композиция фабрик выполняется в модуле-потребителе: screen, layout или другом модуле группы «Композиция». Business-модули не импортируют runtime-код друг друга напрямую, а cross-domain зависимости получают только через аргументы фабрик.
Композиция фабрик выполняется в модуле-потребителе на слое `compositions`: page, layout, screen, widget или другом composition module.
## Структура screen-модуля
Business-модули не импортируют runtime-код друг друга напрямую, а cross-domain зависимости получают только через аргументы фабрик.
## Структура page composition
```text
screens/home/
├── home.screen.tsx
compositions/pages/home/
├── home.page.tsx
└── index.ts
```
## Сборка фабрик
Файл: `screens/home/home.screen.tsx`.
Файл: `compositions/pages/home/home.page.tsx`.
```tsx
import { customerFactory } from '@/business/customer'
@@ -32,7 +34,7 @@ const order = orderFactory({ customer })
const { useOrder, OrderCard } = order
export const HomeScreen = () => {
export const HomePage = () => {
const currentOrder = useOrder()
return <OrderCard order={currentOrder} />
@@ -41,12 +43,12 @@ export const HomeScreen = () => {
`customerFactory` создаётся первой, потому что `orderFactory` зависит от части API домена `customer`. Модуль `order` не импортирует `customer` в runtime — зависимость передаётся снаружи.
## Публичный API screen-модуля
## Публичный API page composition
Файл: `screens/home/index.ts`.
Файл: `compositions/pages/home/index.ts`.
```ts
export { HomeScreen } from './home.screen'
export { HomePage } from './home.page'
```
Screen экспортирует только собственный публичный API. Собранные экземпляры business API остаются деталями реализации screen-модуля.
Page composition экспортирует только собственный публичный API. Собранные экземпляры business API остаются деталями реализации модуля.

View File

@@ -5,7 +5,7 @@ description: Пример создания фабрики business-модуля
# Создание фабрики
Раздел показывает, как оформить фабрику business-модуля в React-проекте: описать публичный API, зависимости и функцию, возвращающую runtime API.
Раздел показывает, как оформить фабрику business-модуля в React-проекте: описать публичный API, зависимости и функцию, возвращающую API фабрики.
## Структура business-модуля
@@ -25,7 +25,7 @@ business/customer/
## Тип публичного API
Публичный API описывает runtime-возможности, которые модуль отдаёт потребителям: хуки, компоненты и сценарные методы.
Публичный API описывает возможности, которые модуль отдаёт потребителям через фабрику: хуки, компоненты и сценарные методы.
```ts
// business/customer/types/customer-api.type.ts

View File

@@ -22,6 +22,7 @@ export const mounts = [
{ target: 'examples/react/factory.md', source: 'canons/examples/react/factory.md' },
{ target: 'examples/react/factory-composition.md', source: 'canons/examples/react/factory-composition.md' },
{ target: 'examples/react/composition-provider.md', source: 'canons/examples/react/composition-provider.md' },
{ target: 'examples/react/composition-structures.md', source: 'canons/examples/react/composition-structures.md' },
];
export const sidebar = [
@@ -41,6 +42,7 @@ export const sidebar = [
{ text: 'Создание фабрики', link: '/examples/react/factory' },
{ text: 'Композиция фабрик', link: '/examples/react/factory-composition' },
{ text: 'Композиция через Provider', link: '/examples/react/composition-provider' },
{ text: 'Структуры compositions', link: '/examples/react/composition-structures' },
],
},
];