- обновлена модель слоёв на app → compositions → business → infra → ui → shared - добавлены правила composition modules и providers-сегмента - обновлены правила монорепозитория для слоя compositions - переписаны React-примеры под page-level композицию - добавлен пример вариантов структуры compositions
12 KiB
title, description
| title | description |
|---|---|
| Слои | Иерархия слоёв от app до shared, правила зависимостей и зона ответственности каждого слоя |
Слои
Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом.
Определение
Слой — уровень организации кода внутри src/. Каждый слой отвечает за свою область и задаёт правила для кода внутри: направление импортов, именование, допустимые связи между модулями.
Группы слоёв
Слои делятся на три группы:
| Группа | Слои | Описание |
|---|---|---|
| Композиция | app, compositions |
Подключают приложение к фреймворку и собирают страницы, маршруты и крупные продуктовые части интерфейса |
| Ядро | business, infra, ui |
Реализация продукта: бизнес-домены, техсервисы, UI-кит |
| Фундамент | shared |
Общие ресурсы: утилиты, хелперы, стили, конфиги |
Направление зависимостей
Любой импорт между модулями — только через публичный API.
app → compositions → business → infra → ui → shared
appподключает приложение к фреймворку и импортирует готовые модули из нижних слоёвcompositionsимпортируетbusiness,infra,ui,sharedbusinessимпортируетinfra,ui,sharedinfraимпортируетinfra,ui,shareduiимпортируетuiиsharedsharedне импортирует другие 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 и продуктовую задачу.
Базовая рекомендация:
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, если они нужны всей странице.
compositions/pages/profile/
├── profile.page.tsx
├── profile-business-composition.ts
├── providers/
├── hooks/
├── stores/
├── types/
└── index.ts
Layout, screen и widget могут получать данные page composition через публичный API соответствующего composition module.
import { useProfilePageStore } from '@/compositions/pages/profile'
Внутри compositions направление импортов между composition modules не фиксируется. Допустим граф, но все импорты идут только через public API.
// Хорошо
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/ — составные блоки.
src/business/
├── auth/
├── catalog/
├── orders/
├── checkout/
└── chat/
Когда количество доменов затрудняет навигацию — вводится группировка по субдоменам. Группа — папка для организации, не модуль (без index.ts).
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/ — общие ресурсы (утилиты, хелперы, стили, конфиги).
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.
src/ui/
├── button/
├── input/
├── icon/
├── carousel/
├── modal/
├── toast/
├── dropdown/
├── tabs/
└── tooltip/
Когда количество компонентов затрудняет навигацию — вводится группировка на примитивы и композиции. Примитивы (button, icon, input) не импортируют композиции. Композиции (carousel, modal, dropdown) строятся на примитивах.
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/, а не здесь.
src/shared/
├── lib/
├── types/
├── styles/
└── sprites/
Требования
- Не имеет runtime-состояния
- Не знает о продуктовых composition modules