--- title: Монорепозитории description: Правила применения SLM Design для frontend-проектов, находящихся в монорепозитории --- # Монорепозитории Раздел описывает, как применять SLM Design, когда фронтенд-проекты находятся в одном монорепозитории. В нём показано, что остаётся внутри приложений, что можно выносить в `packages/` и какие ограничения действуют для общих пакетов. ## Определение **Монорепозиторий — внешний уровень организации нескольких фронтенд-приложений и общих пакетов. SLM применяется внутри каждого приложения, а frontend-пакеты, относящиеся к SLM, содержат переиспользуемый код, вынесенный из слоёв `ui`, `infra` и `shared`.** ## Базовая структура Каждое приложение внутри `apps/` сохраняет собственную SLM-структуру в `src/`. ```text repo/ ├── apps/ │ ├── web/ │ │ └── src/ │ │ ├── app/ │ │ ├── layouts/ │ │ ├── screens/ │ │ ├── widgets/ │ │ ├── business/ │ │ ├── infra/ │ │ ├── ui/ │ │ └── shared/ │ └── admin/ │ └── src/ │ └── ... └── packages/ ├── ui/ │ ├── button/ # самостоятельный пакет UI-модуля │ ├── input/ # самостоятельный пакет UI-модуля │ └── modal/ # самостоятельный пакет UI-модуля ├── infra/ │ ├── theme/ # самостоятельный пакет infra-модуля │ ├── backend-api/ # самостоятельный пакет infra-модуля │ └── logger/ # самостоятельный пакет infra-модуля └── shared/ # единый shared-пакет ├── package.json └── src/ ├── lib/ # переиспользуемые утилиты ├── helpers/ # переиспользуемые helpers └── index.ts ``` `apps/{app}/src` — граница SLM-приложения. `packages/*` находятся выше SLM и не добавляют новые архитектурные слои. ## Группировка frontend-пакетов Frontend-пакеты, вынесенные из SLM-приложений, рекомендуется группировать по источнику кода: `ui`, `infra`, `shared`. ```text packages/ui/* # пакеты UI-модулей packages/infra/* # пакеты infra-модулей packages/shared # единый shared-пакет ``` Эта группировка повторяет названия SLM-слоёв для навигации, но сама не является слоистой архитектурой внутри `packages/`. Монорепозиторий может содержать другие пакеты: tooling, конфиги, SDK, схемы, e2e и другие технические пакеты вне SLM. ## Пакет и модуль Пакет не равен SLM-модулю: модуль — архитектурная единица внутри слоя приложения, package — единица монорепозитория для переиспользования, владения, сборки и публикации. В `packages/ui/*` размещаются пакеты самостоятельных UI-модулей. В `packages/infra/*` размещаются пакеты самостоятельных инфраструктурных модулей. `packages/shared` устроен иначе: это единый пакет для переиспользуемых утилит, helpers и другого фундаментального кода без привязки к конкретному приложению. ```text packages/ui/button/ packages/ui/modal/ packages/infra/theme/ packages/infra/backend-api/ packages/shared/ ``` ## Что остаётся в приложении Слои `app`, `layouts`, `screens`, `widgets` и `business` остаются внутри конкретного приложения. ```text apps/web/src/app/ apps/web/src/layouts/ apps/web/src/screens/ apps/web/src/widgets/ apps/web/src/business/ ``` `app`, `layouts` и `screens` привязаны к роутингу, каркасу и страницам конкретного приложения. `widgets` не выносятся в пакеты, потому что это слой композиции интерфейса приложения. `business` не выносится в `packages/*`. Домены остаются рядом со сценариями приложения, чтобы не превращать монорепозиторий в общий бизнес-слой. ## Что можно выносить В пакеты выносится только код из `ui`, `infra` и `shared`, который потенциально будет использоваться в двух и более фронтенд-приложениях монорепозитория. | Группа | Что выносить | Пример | |--------|--------------|--------| | `packages/ui/*` | Самостоятельные UI-модули без бизнес-логики | `packages/ui/button` | | `packages/infra/*` | Самостоятельные технические сервисы | `packages/infra/backend-api` | | `packages/shared` | Общие утилиты, helpers и фундаментальный код | `packages/shared` | Пакет можно создавать сразу, если модуль имеет общую природу и ожидается его переиспользование между приложениями. App-specific код остаётся внутри приложения. ## UI-пакеты В `packages/ui/*` размещаются переиспользуемые UI-модули. ```text packages/ui/button/ ├── package.json └── src/ ├── button.tsx ├── styles/ ├── types/ └── index.ts ``` UI-пакет не содержит бизнес-логику, обращения к API, сценарные хуки приложения и композицию страниц. ## Infra-пакеты В `packages/infra/*` размещаются переиспользуемые инфраструктурные модули. ```text packages/infra/backend-api/ ├── package.json └── src/ ├── clients/ ├── config/ ├── types/ └── index.ts ``` Привязанные к конкретному приложению сервисы остаются в `apps/{app}/src/infra`. Например, локализация со словарями конкретного продукта остаётся в приложении; общим пакетом может быть только переиспользуемый i18n-движок. ## Shared-пакет `packages/shared` является единым пакетом. ```text packages/shared/ ├── package.json └── src/ ├── lib/ ├── helpers/ └── index.ts ``` В `packages/shared` сразу выносится общий фундаментальный код: чистые функции, helpers, утилиты, независимые константы и другой код без знания о продукте. Проектные стили, типы приложения, продуктовые конфиги и ресурсы, завязанные на одно приложение, в общий `shared` не выносятся. ## Имена пакетов и импорты Путь импорта задаётся `name` в `package.json`, а не расположением директории. ```json { "name": "@repo/theme" } ``` ```text packages/infra/theme/package.json ``` ```ts import { ThemeProvider } from '@repo/theme' ``` Пакеты должны импортироваться только через публичный API. Deep imports внутрь пакета запрещены. ```ts // Хорошо import { Button } from '@repo/button' // Плохо import { Button } from '@repo/button/src/button' ``` ## Зависимости На уровне монорепозитория приложения зависят от пакетов, а пакеты не зависят от приложений. ```text apps → packages packages -/→ apps ``` Внутри приложения продолжает действовать обычное направление зависимостей SLM. ```text app → [ layouts | screens ] → widgets → business → infra → ui → shared ``` Пакеты не должны нарушать природу своей группы: `packages/ui/*` не импортирует `packages/infra/*`, `packages/shared` не импортирует другие группы, а `packages/infra/*` не знает о приложениях. ## Когда не выносить Не выносите код в пакет, если он не может быть использован в двух и более фронтенд-приложениях, зависит от роутинга или страниц, содержит бизнес-логику, отражает продуктовую композицию конкретного интерфейса или не имеет стабильного публичного API. Фактическое использование в одном приложении не запрещает пакет, если модуль имеет общую природу и потенциально нужен нескольким приложениям. ```text # Плохо apps/web/src/screens/home/parts/promo-section/ packages/ui/promo-section/ ``` Если блок нужен только одной странице или отражает продуктовую композицию конкретного приложения, он остаётся локальным `parts/`-модулем. ## Конфигурационные пакеты Конфигурационные пакеты не относятся к SLM-архитектуре. Если в монорепозитории есть общие настройки TypeScript, ESLint, сборки или форматирования, они относятся к tooling-инфраструктуре репозитория. Такие пакеты могут находиться в `packages/`, но их структура зависит от выбранного инструментария и не участвует в правилах слоёв внутри `src/`. ## Правила - SLM применяется внутри каждого `apps/{app}/src`. - Frontend-пакеты, вынесенные из SLM-приложений, группируются в `packages/ui`, `packages/infra`, `packages/shared`. - Группы `packages/ui`, `packages/infra`, `packages/shared` не являются SLM-слоями. - В `packages/ui/*` размещаются пакеты самостоятельных UI-модулей. - В `packages/infra/*` размещаются пакеты самостоятельных инфраструктурных модулей. - `packages/shared` является единым пакетом для переиспользуемых утилит и helpers. - Модуль можно размещать в пакете, если он потенциально будет использоваться в двух и более фронтенд-приложениях. - `business`, `app`, `layouts`, `screens`, `widgets` не выносятся в пакеты. - Проектные стили, типы приложения и продуктовые конфиги не выносятся в `packages/shared`. - Пакеты не импортируют приложения. - Межпакетные импорты идут только через публичный API. - Deep imports внутрь пакетов запрещены. - Локальная колокация важнее преждевременного выноса в `packages/*`.