- обновлена модель слоёв на app → compositions → business → infra → ui → shared - добавлены правила composition modules и providers-сегмента - обновлены правила монорепозитория для слоя compositions - переписаны React-примеры под page-level композицию - добавлен пример вариантов структуры compositions
248 lines
12 KiB
Markdown
248 lines
12 KiB
Markdown
---
|
||
title: Слои
|
||
description: Иерархия слоёв от app до shared, правила зависимостей и зона ответственности каждого слоя
|
||
---
|
||
|
||
# Слои
|
||
|
||
Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом.
|
||
|
||
## Определение
|
||
|
||
**Слой — уровень организации кода внутри `src/`. Каждый слой отвечает за свою область и задаёт правила для кода внутри: направление импортов, именование, допустимые связи между модулями.**
|
||
|
||
## Группы слоёв
|
||
|
||
Слои делятся на три группы:
|
||
|
||
| Группа | Слои | Описание |
|
||
|--------|------|----------|
|
||
| Композиция | `app`, `compositions` | Подключают приложение к фреймворку и собирают страницы, маршруты и крупные продуктовые части интерфейса |
|
||
| Ядро | `business`, `infra`, `ui` | Реализация продукта: бизнес-домены, техсервисы, UI-кит |
|
||
| Фундамент | `shared` | Общие ресурсы: утилиты, хелперы, стили, конфиги |
|
||
|
||
## Направление зависимостей
|
||
|
||
Любой импорт между модулями — только через публичный API.
|
||
|
||
```text
|
||
app → compositions → business → infra → ui → shared
|
||
```
|
||
|
||
- `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
|
||
|
||
Точка входа приложения. Отвечает за запуск, роутинг и подключение composition modules к фреймворку.
|
||
|
||
В отличие от остальных слоёв, `app/` не содержит модулей SLM. Здесь живут только инфраструктурные файлы, которые не могут быть никаким другим слоем: файлы фреймворка роутинга, точка запуска и код инициализации.
|
||
|
||
### Требования
|
||
|
||
- Не содержит модулей SLM — только файлы фреймворка, роутинг, инициализация
|
||
- Содержит: файлы маршрутов, bootstrap, обработку ошибок верхнего уровня (404, error boundary), подключение глобальных стилей и ассетов
|
||
- Провайдеры, guards, layouts, screens и страницы — только подключает готовые из `compositions` или нижних слоёв, не реализует
|
||
- Не содержит бизнес-логику, UI-компоненты, хуки, сторы, сервисы
|
||
- Никем не импортируется
|
||
|
||
## Слой Compositions
|
||
|
||
`compositions/` — слой сборки страниц, маршрутов и крупных продуктовых частей интерфейса.
|
||
|
||
На этом слое собираются page, layout, screen, widget и другие composition modules. Они связываются между собой и с нижними слоями: `business`, `infra`, `ui`, `shared`.
|
||
|
||
SLM не фиксирует жёсткую структуру внутри `compositions`. Команда выбирает организацию под фреймворк, роутинг, CMS и продуктовую задачу.
|
||
|
||
Базовая рекомендация:
|
||
|
||
```text
|
||
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'
|
||
```
|
||
|
||
### Требования
|
||
|
||
- `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/`. Каждый бизнес-модуль создаёт публичный API фабрики в корне. Cross-domain зависимости: runtime — через аргументы фабрики, типы — напрямую через `import type`.
|
||
|
||
Business объединяет то, что в FSD разделено на `features` и `entities`: пользовательские сценарии и бизнес-сущности живут вместе, внутри одного домена. Внутри домена сегменты разделяют ответственность: `types/` — доменная модель, `hooks/` и `services/` — сценарии и логика, `mappers/` — трансформация данных, `parts/` — составные блоки.
|
||
|
||
```text
|
||
src/business/
|
||
├── auth/
|
||
├── catalog/
|
||
├── orders/
|
||
├── checkout/
|
||
└── chat/
|
||
```
|
||
|
||
Когда количество доменов затрудняет навигацию — вводится группировка по субдоменам. Группа — папка для организации, не модуль (без `index.ts`).
|
||
|
||
```text
|
||
src/business/
|
||
├── commerce/
|
||
│ ├── catalog/
|
||
│ ├── cart/
|
||
│ ├── orders/
|
||
│ └── checkout/
|
||
└── communication/
|
||
├── chat/
|
||
└── notifications/
|
||
```
|
||
|
||
### Требования
|
||
|
||
- Один модуль = один бизнес-домен
|
||
- Циклические зависимости между доменами запрещены
|
||
- Публичный API фабрики — через фабрику в корне модуля (`{name}.factory.ts`). `index.ts` экспортирует только фабрику и type-only экспорты
|
||
- Импорт runtime-кода между доменами — через фабрику. `import type` — напрямую
|
||
- Доменные типы (`User`, `Product`) живут здесь, не в `shared/`
|
||
|
||
## Слой infra
|
||
|
||
Техсервисы приложения: theme, i18n, API-адаптеры, logger, realtime. Каждый сервис — отдельный модуль.
|
||
|
||
Слой входит в группу «Ядро». Импортирует `infra/`, `ui/`, `shared/`.
|
||
|
||
Отличие от `shared/`: infra — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги).
|
||
|
||
```text
|
||
src/infra/
|
||
├── theme/
|
||
├── i18n/
|
||
├── backend-api/
|
||
├── maps-api/
|
||
├── logger/
|
||
├── feature-flags/
|
||
└── realtime/
|
||
```
|
||
|
||
### Требования
|
||
|
||
- Один модуль = один техсервис
|
||
- Импортирует `infra/`, `ui/`, `shared/`
|
||
- Не содержит продуктовые composition modules конкретных страниц или маршрутов
|
||
|
||
## Слой UI
|
||
|
||
UI-кит без бизнес-логики: button, carousel, toast, modal.
|
||
|
||
Слой входит в группу «Ядро». Импортирует `ui/` и `shared/`.
|
||
|
||
Компоненты строятся друг на друге: `button` использует `icon`, `carousel` использует `button`.
|
||
|
||
```text
|
||
src/ui/
|
||
├── button/
|
||
├── input/
|
||
├── icon/
|
||
├── carousel/
|
||
├── modal/
|
||
├── toast/
|
||
├── dropdown/
|
||
├── tabs/
|
||
└── tooltip/
|
||
```
|
||
|
||
Когда количество компонентов затрудняет навигацию — вводится группировка на примитивы и композиции. Примитивы (`button`, `icon`, `input`) не импортируют композиции. Композиции (`carousel`, `modal`, `dropdown`) строятся на примитивах.
|
||
|
||
```text
|
||
src/ui/
|
||
├── primitives/
|
||
│ ├── button/
|
||
│ ├── input/
|
||
│ ├── icon/
|
||
│ └── badge/
|
||
└── composites/
|
||
├── carousel/
|
||
├── modal/
|
||
├── dropdown/
|
||
├── tabs/
|
||
└── tooltip/
|
||
```
|
||
|
||
### Требования
|
||
|
||
- Не содержит бизнес-логику
|
||
- Импортирует только `ui/` и `shared/`
|
||
|
||
## Слой Shared
|
||
|
||
Общие ресурсы: утилиты, хелперы, стили, конфиги. Не знает о бизнес-домене.
|
||
|
||
Слой входит в группу «Фундамент» — ни о ком не знает, никого не импортирует.
|
||
|
||
Отличие от `infra/`: infra — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги).
|
||
|
||
Отличие от `ui/`: UI-компоненты (button, carousel, modal) живут в слое `ui/`, а не здесь.
|
||
|
||
```text
|
||
src/shared/
|
||
├── lib/
|
||
├── types/
|
||
├── styles/
|
||
└── sprites/
|
||
```
|
||
|
||
### Требования
|
||
|
||
- Не имеет runtime-состояния
|
||
- Не знает о продуктовых composition modules
|