mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
sync
This commit is contained in:
75
docs/README.md
Normal file
75
docs/README.md
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
layout: home
|
||||
title: SLM Design
|
||||
description: Архитектура фронтенд-приложений с явным владением ответственностями
|
||||
|
||||
hero:
|
||||
name: SLM Design
|
||||
text: Архитектура владения ответственностями
|
||||
tagline: Сначала определяется ответственность и её владелец. Слои, группы, модули и сегменты выражают уже принятое архитектурное решение.
|
||||
image:
|
||||
src: /logo.svg
|
||||
alt: SLM Design
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Изучить архитектуру
|
||||
link: /architecture/
|
||||
- theme: alt
|
||||
text: Открыть правила
|
||||
link: /rules/registry
|
||||
|
||||
features:
|
||||
- title: Ответственность раньше структуры
|
||||
details: Модуль появляется из самостоятельной ответственности, а не из размера каталога, количества файлов или выбранного фреймворка.
|
||||
- title: Явная структурная модель
|
||||
details: Слой определяет роль, группа классифицирует модули, модуль владеет ответственностью, сегмент организует реализацию.
|
||||
- title: Проверяемые границы
|
||||
details: Публичные API, направление зависимостей и правила жизненного цикла делают архитектурное решение наблюдаемым и проверяемым.
|
||||
---
|
||||
|
||||
SLM Design (Scoped Layered Module Design) — структурная архитектура фронтенд-приложений, основанная на явном владении ответственностями.
|
||||
|
||||
Архитектурное решение начинается не с папки или имени файла. Сначала определяется ответственность, затем её владелец, роль владельца в приложении и только после этого физическое размещение кода.
|
||||
|
||||
## Основа
|
||||
|
||||
SLM использует четыре структурных понятия:
|
||||
|
||||
1. **Слой** классифицирует код по архитектурной роли и ограничивает направление зависимостей.
|
||||
2. **Группа** помогает классифицировать модули внутри слоя, но ничего не реализует и ничем не владеет.
|
||||
3. **Модуль** владеет самостоятельной ответственностью, её публичным API, зависимостями, состоянием и жизненным циклом.
|
||||
4. **Сегмент** организует внутреннее содержимое одного модуля и не создаёт нового владельца.
|
||||
|
||||
```text
|
||||
Слой → [Группа*] → Модуль → [Сегмент*]
|
||||
```
|
||||
|
||||
Знак `*` означает, что элементов может не быть или их может быть несколько. Группы могут быть вложены друг в друга внутри одного слоя. Сегменты всегда остаются внутри одного модуля.
|
||||
|
||||
## Документация
|
||||
|
||||
### Архитектура
|
||||
|
||||
- [Обзор архитектуры](./architecture/)
|
||||
- [Слои](./architecture/layers.md)
|
||||
- [Модули](./architecture/modules.md)
|
||||
- [Сегменты](./architecture/segments.md)
|
||||
|
||||
### Правила
|
||||
|
||||
- [Как устроены правила](./rules/)
|
||||
- [Реестр правил](./rules/registry.md)
|
||||
|
||||
### Справочные материалы
|
||||
|
||||
- [Терминология](./reference/terminology.md)
|
||||
- [Проверка архитектуры](./reference/validation.md)
|
||||
|
||||
## Порядок принятия решения
|
||||
|
||||
1. Сформулировать ответственность без упоминания папок, файлов и библиотек.
|
||||
2. Назначить одного владельца ответственности.
|
||||
3. Выбрать слой по роли владельца.
|
||||
4. Определить публичный контракт, зависимости, состояние и жизненный цикл.
|
||||
5. Организовать реализацию сегментами, если это упрощает навигацию.
|
||||
6. Представить принятое решение папками, файлами и публичными точками входа.
|
||||
84
docs/architecture/README.md
Normal file
84
docs/architecture/README.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Архитектура SLM
|
||||
|
||||
SLM описывает владение ответственностями внутри одного фронтенд-приложения. Слой определяет роль кода, группа классифицирует модули, модуль владеет ответственностью, а сегмент организует реализацию владельца.
|
||||
|
||||
## Владение как основа
|
||||
|
||||
**Ответственность** — связная часть приложения с одной причиной изменяться. Она становится самостоятельной, когда ей нужны собственный публичный контракт, зависимости, состояние или область жизни.
|
||||
|
||||
У каждой самостоятельной ответственности есть ровно один владелец. В SLM владельцем является модуль. Он определяет:
|
||||
|
||||
- какие возможности доступны внешним потребителям;
|
||||
- от каких других возможностей зависит ответственность;
|
||||
- кому принадлежат данные и изменяемое состояние;
|
||||
- когда создаются и уничтожаются долгоживущие ресурсы;
|
||||
- как устроена внутренняя реализация.
|
||||
|
||||
Место выполнения кода не меняет владельца. Компонент, провайдер, маршрут или точка запуска могут технически вызывать код ответственности, но не получают владение ею автоматически.
|
||||
|
||||
## Структурная модель
|
||||
|
||||
```text
|
||||
SLM root
|
||||
└── слой
|
||||
├── модуль
|
||||
│ └── сегмент
|
||||
└── группа
|
||||
├── модуль
|
||||
└── группа
|
||||
└── модуль
|
||||
└── сегмент
|
||||
```
|
||||
|
||||
| Сущность | Назначение | Владеет ответственностью |
|
||||
|---|---|---|
|
||||
| Слой | Классифицирует код по архитектурной роли | Нет |
|
||||
| Группа | Классифицирует модули внутри слоя | Нет |
|
||||
| Модуль | Реализует одну самостоятельную ответственность | Да |
|
||||
| Сегмент | Организует внутренности одного модуля | Нет |
|
||||
|
||||
Слой и группа находятся снаружи модульной границы. Сегмент находится внутри неё. Компоненты, хуки, сервисы, хранилища и другие детали реализации принадлежат ближайшему модулю-владельцу, если сами не образуют вложенный модуль.
|
||||
|
||||
## Порядок проектирования
|
||||
|
||||
Архитектурное решение принимается от смысла к структуре:
|
||||
|
||||
1. Описать результат или поведение, за которое должен отвечать код.
|
||||
2. Определить одну причину изменения этой ответственности.
|
||||
3. Найти связанные данные, поведение, состояние и жизненный цикл.
|
||||
4. Назначить модуль владельцем и определить его внешних потребителей.
|
||||
5. Выбрать [слой](./layers.md) по роли ответственности.
|
||||
6. Спроектировать публичный API и допустимые зависимости [модуля](./modules.md).
|
||||
7. При необходимости организовать реализацию [сегментами](./segments.md).
|
||||
8. Только после этого выбрать физические пути и имена файлов.
|
||||
|
||||
Если ответственность или владелец не определены, файловая структура не может исправить архитектурную неопределённость.
|
||||
|
||||
## Логическая и физическая границы
|
||||
|
||||
Модуль не определяется наличием папки, `index.ts` или нескольких файлов. Его определяет самостоятельная ответственность и владение её контрактом, зависимостями, состоянием и жизненным циклом.
|
||||
|
||||
После принятия решения модуль получает отдельную папку и публичные точки входа. Это обязательное физическое представление модульной границы, необходимое потребителям и автоматическим проверкам.
|
||||
|
||||
Верны обе формулировки:
|
||||
|
||||
- самостоятельная ответственность требует модульной границы;
|
||||
- отдельная папка сама по себе не доказывает наличие модуля.
|
||||
|
||||
Пути сопоставляются со слоями, группами, модулями и сегментами в стайлгайде или конфигурации конкретного проекта. Имя пути не меняет нормативный смысл сущности.
|
||||
|
||||
## Область применения
|
||||
|
||||
SLM применяется внутри **SLM root** — границы структурной архитектуры одного приложения. Это может быть `src/` или другая область, установленная проектом.
|
||||
|
||||
Архитектура определяет:
|
||||
|
||||
- роли слоёв и допустимые направления зависимостей;
|
||||
- владельцев самостоятельных ответственностей;
|
||||
- публичные границы модулей;
|
||||
- назначение групп и сегментов;
|
||||
- владение состоянием и жизненным циклом ресурсов.
|
||||
|
||||
SLM не задаёт обязательный поток данных, полный файловый стайлгайд, фиксированный набор сегментов, правила монорепозиториев или обязательную внутреннюю форму каждого модуля.
|
||||
|
||||
Нормативный смысл терминов находится в [терминологии](../reference/terminology.md). Точные блокирующие требования объявлены только в [реестре правил](../rules/registry.md).
|
||||
120
docs/architecture/layers.md
Normal file
120
docs/architecture/layers.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Слои
|
||||
|
||||
Слой классифицирует код по архитектурной роли и задаёт допустимые направления зависимостей. Слой не владеет ответственностью: владельцем остаётся модуль, размещённый в этом слое.
|
||||
|
||||
Слой выбирается после определения ответственности. Похожее имя папки или наличие зависимости от конкретной библиотеки не являются основанием для выбора слоя.
|
||||
|
||||
## Роли слоёв
|
||||
|
||||
SLM определяет шесть ролей:
|
||||
|
||||
| Слой | Роль |
|
||||
|---|---|
|
||||
| `app` | Связь приложения с фреймворком: запуск, маршруты, преобразование внешних входных данных и подключение готовых публичных API |
|
||||
| `compositions` | Сборка продуктового интерфейса: страницы, макеты, экраны, виджеты и другие композиции |
|
||||
| `domains` | Предметные модели, правила, сценарии и продуктовое состояние |
|
||||
| `infra` | Технические сервисы и возможности приложения без собственной предметной модели |
|
||||
| `ui` | Универсальные интерфейсные модули без зависимости от конкретной продуктовой композиции |
|
||||
| `shared` | Детерминированный фундамент без знания о продукте, изменяемого состояния и ввода-вывода |
|
||||
|
||||
Отсутствующая роль не требует пустой папки. Проект создаёт слой только тогда, когда в нём появляется соответствующая ответственность.
|
||||
|
||||
### App
|
||||
|
||||
`app` содержит точки связи с фреймворком: запуск, файлы маршрутов и преобразование внешних входных данных. Они подключают готовые публичные API других слоёв, но не присваивают их ответственность.
|
||||
|
||||
Точки входа `app` являются специальным немодульным исключением. Страница, макет, провайдер или другой самостоятельный продуктовый владелец реализуется в подходящем модуле и только подключается из `app`.
|
||||
|
||||
### Compositions
|
||||
|
||||
`compositions` содержит владельцев продуктовых композиций: страниц, макетов, экранов, виджетов, результатов маршрутов и интерфейса, объединяющего несколько ответственностей.
|
||||
|
||||
Конкретная организация слоя определяется продуктом и фреймворком. Названия `pages`, `layouts`, `screens` и `widgets` могут использоваться как группы, но не являются дополнительными слоями.
|
||||
|
||||
### Domains
|
||||
|
||||
`domains` содержит модули-владельцы предметных ответственностей: моделей, правил, сценариев и продуктового состояния.
|
||||
|
||||
Доменный модуль является обычным SLM-модулем. Ему не требуется отдельная архитектурная форма только потому, что он находится в `domains`.
|
||||
|
||||
### Infra
|
||||
|
||||
`infra` содержит технические возможности приложения: аналитику, локализацию, тему, телеметрию, интеграции с платформой и другие сервисы без собственной предметной модели.
|
||||
|
||||
Технический способ выполнения предметного сценария не переносит владение сценарием из `domains` в `infra`.
|
||||
|
||||
### UI
|
||||
|
||||
`ui` содержит универсальные интерфейсные модули, которые не знают о конкретной странице, маршруте или продуктовой композиции.
|
||||
|
||||
### Shared
|
||||
|
||||
`shared` содержит детерминированный фундамент, не зависящий от продукта и не имеющий ввода-вывода, изменяемого состояния или жизненного цикла.
|
||||
|
||||
В `shared` могут находиться обычные модули и небольшие немодульные ресурсы: чистые функции, общие типы, стили, декларативная конфигурация и статические файлы.
|
||||
|
||||
## Направление зависимостей
|
||||
|
||||
Матрица определяет, от каких слоёв может зависеть исходный слой:
|
||||
|
||||
| Исходный слой | Допустимые целевые слои |
|
||||
|---|---|
|
||||
| `app` | `app`, `compositions`, `domains`, `infra`, `ui`, `shared` |
|
||||
| `compositions` | `compositions`, `domains`, `infra`, `ui`, `shared` |
|
||||
| `domains` | `domains`, `infra`, `ui`, `shared` |
|
||||
| `infra` | `infra`, `ui`, `shared` |
|
||||
| `ui` | `ui`, `shared` |
|
||||
| `shared` | `shared` |
|
||||
|
||||
Разрешённая зависимость может пропускать промежуточные слои. Например, `compositions` может напрямую использовать модуль `ui`, не создавая посредника в `domains` или `infra`.
|
||||
|
||||
Обычный импорт, импорт типа (`import type`) и реэкспорт одинаково участвуют в архитектурном графе. Для связи между модулями дополнительно действуют их [публичные границы и запрет циклов](./modules.md#зависимости-между-модулями).
|
||||
|
||||
Разрешённое направление не переносит владение. Если модуль `domains` использует `infra`, предметный сценарий остаётся ответственностью доменного модуля, а техническая возможность — ответственностью инфраструктурного.
|
||||
|
||||
`infra` может использовать `ui`, когда технической возможности нужно собственное визуальное представление: CAPTCHA, uploader, карта или инструмент разработчика. `ui` не использует `infra`; необходимые технические возможности универсальный UI получает через входной контракт.
|
||||
|
||||
## Группировка модулей
|
||||
|
||||
Группа классифицирует модули внутри одного слоя или другой группы. Она нужна, когда плоский список модулей перестаёт быть понятным.
|
||||
|
||||
```text
|
||||
compositions/
|
||||
├── pages/ # Группа
|
||||
│ ├── catalog/ # Модуль
|
||||
│ └── profile/ # Модуль
|
||||
├── layouts/ # Группа
|
||||
│ └── main/ # Модуль
|
||||
└── widgets/ # Группа
|
||||
└── cart-summary/ # Модуль
|
||||
```
|
||||
|
||||
Группа:
|
||||
|
||||
- содержит только модули и вложенные группы;
|
||||
- не владеет ответственностью или реализацией;
|
||||
- не имеет состояния и жизненного цикла;
|
||||
- не предоставляет публичный API;
|
||||
- не является узлом графа зависимостей;
|
||||
- не реэкспортирует содержащиеся в ней модули.
|
||||
|
||||
Модуль может находиться непосредственно в слое. Группа вводится только ради реальной классификации, а её названия и глубину определяет проект.
|
||||
|
||||
Группа организует несколько владельцев внутри слоя. [Сегмент](./segments.md) организует код внутри одного владельца.
|
||||
|
||||
## Немодульные исключения
|
||||
|
||||
Внутри SLM root код по умолчанию принадлежит модулю. Исключения ограничены двумя случаями:
|
||||
|
||||
- точка входа `app` непосредственно связывает приложение с фреймворком;
|
||||
- ресурс `shared` является небольшой самостоятельной детерминированной единицей без внутренней границы.
|
||||
|
||||
Если ресурсу `shared` нужны несколько файлов реализации, собственные архитектурные зависимости, изменяемое состояние, ввод-вывод или жизненный цикл, ему требуется модуль-владелец.
|
||||
|
||||
## Связанные правила
|
||||
|
||||
- [`SLM-LAYER-R001`](../rules/registry.md#slm-layer-r001)
|
||||
- [`SLM-LAYER-A002`](../rules/registry.md#slm-layer-a002)
|
||||
- [`SLM-LAYER-R003`](../rules/registry.md#slm-layer-r003)
|
||||
- [`SLM-GROUP-R007`](../rules/registry.md#slm-group-r007)
|
||||
- [`SLM-MODULE-R011`](../rules/registry.md#slm-module-r011)
|
||||
176
docs/architecture/modules.md
Normal file
176
docs/architecture/modules.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Модули
|
||||
|
||||
Модуль является владельцем самостоятельной ответственности. Публичный API, зависимости, состояние, жизненный цикл и внутренняя реализация ответственности относятся к модулю независимо от того, в каком файле или сущности фреймворка выполняется код.
|
||||
|
||||
## Ответственность и владелец
|
||||
|
||||
Перед созданием модуля ответственность формулируется без упоминания желаемой папки, файла или библиотеки.
|
||||
|
||||
Самостоятельность ответственности определяется вопросами:
|
||||
|
||||
- есть ли у неё отдельная причина изменяться;
|
||||
- нужен ли внешним потребителям собственный контракт;
|
||||
- есть ли у неё архитектурные зависимости;
|
||||
- владеет ли она данными или изменяемым состоянием;
|
||||
- нужна ли ей собственная область жизни;
|
||||
- можно ли назвать её независимо от внутренней реализации.
|
||||
|
||||
Если ответственность самостоятельна, она получает ровно один модуль-владелец. Если несколько частей изменяются по несвязанным причинам, им нужны разные владельцы.
|
||||
|
||||
Размер не определяет модуль. Модуль может состоять из одного файла реализации, а большая папка может оставаться частью другого владельца.
|
||||
|
||||
## Граница владения
|
||||
|
||||
Модуль определяет:
|
||||
|
||||
- публичные возможности ответственности;
|
||||
- допустимые внешние зависимости;
|
||||
- модели и правила, принадлежащие ответственности;
|
||||
- состояние и источник истины;
|
||||
- создание и очистку долгоживущих ресурсов;
|
||||
- устройство внутренней реализации.
|
||||
|
||||
Компонент, хук, провайдер, хранилище, сервис или маршрут могут выполнять часть этой работы технически. Владение остаётся у ближайшего модуля, пока часть кода не получает самостоятельную ответственность и собственную модульную границу.
|
||||
|
||||
## Публичный API
|
||||
|
||||
У модуля один логический публичный API. Он является контрактом между владельцем и внешними потребителями, а не перечнем всех внутренних файлов.
|
||||
|
||||
Публичный API:
|
||||
|
||||
- открывает только возможности, необходимые реальным внешним потребителям;
|
||||
- скрывает детали реализации и изменяемые внутренние механизмы;
|
||||
- не раскрывает внутренние сегменты;
|
||||
- представлен объявленными публичными фасетами;
|
||||
- является единственным способом доступа к модулю извне.
|
||||
|
||||
Внутри своей границы модуль может обращаться к собственным файлам напрямую. Требование публичного API действует при пересечении модульной границы.
|
||||
|
||||
### Фасеты
|
||||
|
||||
Фасет — публичная точка входа, открывающая часть единого API для определённой среды выполнения.
|
||||
|
||||
| Фасет | Назначение |
|
||||
|---|---|
|
||||
| `index` | Универсальные типы и исполняемый код, совместимый с серверным рендерингом, включая RSC, и клиентским выполнением |
|
||||
| `client` | Клиентская граница фреймворка, участвующая в серверной предварительной отрисовке и выполняющаяся при гидратации и в браузере |
|
||||
| `browser` | Код только для браузера, подключаемый динамически через границу с отключённым SSR |
|
||||
| `server` | Код только для сервера, недоступный универсальной и клиентской среде выполнения |
|
||||
|
||||
`index` обязателен. Остальные фасеты создаются только при наличии реального потребителя и несовместимых требований к среде.
|
||||
|
||||
Ограничение среды распространяется на все исполняемые импорты и реэкспорты фасета, включая транзитивные. Имя файла, директива `use client`, tree shaking или проверка `typeof window` сами по себе не доказывают совместимость.
|
||||
|
||||
Один исполняемый экспорт размещается в минимально подходящем фасете и не дублируется между ними. Универсальный фасет не реэкспортирует специализированные фасеты.
|
||||
|
||||
```text
|
||||
auth/
|
||||
├── index.ts # Обязательный универсальный фасет
|
||||
├── client.ts # При необходимости
|
||||
├── browser.ts # При необходимости
|
||||
├── server.ts # При необходимости
|
||||
└── ... # Внутренняя реализация
|
||||
```
|
||||
|
||||
Эта файловая форма представляет уже определённую публичную границу. Наличие `index.ts` само по себе не создаёт модуль.
|
||||
|
||||
## Зависимости между модулями
|
||||
|
||||
Зависимость связывает владельца исходного кода с владельцем импортируемого кода. Импорт внутреннего файла, сегмента или компонента относится к ближайшему модулю-владельцу. Вложенный модуль начинает собственную границу зависимостей.
|
||||
|
||||
При пересечении модульной границы код использует только публичный фасет целевого модуля:
|
||||
|
||||
```ts
|
||||
// Допустимо
|
||||
import { Button } from '@/ui/button'
|
||||
|
||||
// Недопустимый глубокий импорт
|
||||
import { Button } from '@/ui/button/button'
|
||||
```
|
||||
|
||||
Для каждой связи выполняются три условия:
|
||||
|
||||
1. Направление разрешено [матрицей слоёв](./layers.md#направление-зависимостей).
|
||||
2. Целевой модуль используется только через публичный API.
|
||||
3. Общий граф модулей остаётся ацикличным.
|
||||
|
||||
Модули одного слоя могут зависеть друг от друга, если соблюдены публичные границы и не возникает цикл. SLM не требует обязательного посредника или отдельного механизма dependency injection для любой межмодульной связи.
|
||||
|
||||
## Компоненты
|
||||
|
||||
Компонент является сущностью фреймворка, реализующей часть интерфейса родительского модуля. Он не образует самостоятельного владельца только из-за наличия отдельного файла, каталога, props, локального состояния, доступа к данным или кода жизненного цикла.
|
||||
|
||||
Зависимости, состояние и ресурсы компонента относятся к родительскому модулю. Если UI-часть получает самостоятельную ответственность, публичный API, собственные зависимости или область жизни, ей требуется модульная граница.
|
||||
|
||||
Модуль может состоять из одного корневого компонента. В этом случае компонент реализует интерфейс, а модуль остаётся владельцем ответственности.
|
||||
|
||||
Компонент может быть оформлен отдельным каталогом и иметь локальный `index.ts`:
|
||||
|
||||
```text
|
||||
button-submit/
|
||||
├── button-submit.tsx
|
||||
├── styles/
|
||||
│ └── button-submit.module.css
|
||||
├── types/
|
||||
│ └── button-submit.types.ts
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Такой `index.ts` является внутренней точкой входа компонента. Он упрощает импорты внутри модуля, но не создаёт SLM public API, нового владельца или узел графа зависимостей.
|
||||
|
||||
| `index.ts` компонента | Публичный фасет модуля |
|
||||
|---|---|
|
||||
| Действует внутри ближайшего модуля | Действует при пересечении модульной границы |
|
||||
| Экспортирует локальную реализацию и типы | Экспортирует контракт владельца |
|
||||
| Не создаёт архитектурную границу | Представляет архитектурную границу |
|
||||
| Не делает компонент модулем | Принадлежит уже определённому модулю |
|
||||
|
||||
Если компонент входит в публичный контракт владельца, корневой фасет модуля явно реэкспортирует его локальную точку входа. Внешний код по-прежнему импортирует модуль, а не внутренний путь компонента.
|
||||
|
||||
## Вложенные модули
|
||||
|
||||
Вложенный модуль — самостоятельный владелец, физически размещённый внутри родительского модуля. Он имеет собственные ответственность, публичный API и границу зависимостей и подчиняется общим правилам модулей.
|
||||
|
||||
Код родительского модуля использует вложенный модуль через его публичный API. Код за пределами родительской границы не импортирует вложенный модуль напрямую и получает необходимые возможности через API родителя.
|
||||
|
||||
Если вложенный модуль становится нужен нескольким внешним владельцам, его можно перенести в минимальную общую область. Сам факт повторного использования внутри родителя переноса не требует.
|
||||
|
||||
## Состояние и жизненный цикл
|
||||
|
||||
Состояние принадлежит модулю, ответственность которого определяет его смысл, допустимые изменения и область жизни. Место хранения состояния не переносит владение в файл хранилища, провайдер или контекст фреймворка.
|
||||
|
||||
Для каждого долгоживущего ресурса модуль-владелец определяет:
|
||||
|
||||
- место создания;
|
||||
- момент запуска;
|
||||
- область жизни;
|
||||
- допустимое число экземпляров;
|
||||
- способ остановки, отмены или освобождения.
|
||||
|
||||
Подписка, обработчик событий, таймер, наблюдатель, запрос или соединение не остаются активными после завершения своей области жизни. Очистку может технически выполнить компонент, провайдер или фреймворк, но ответственность за корректную границу остаётся у модуля.
|
||||
|
||||
Размещение экземпляра на уровне файла не доказывает область жизни всего приложения. Точка входа `app` может запустить ресурс через публичный API модуля, но не становится его владельцем.
|
||||
|
||||
## Внутренняя организация
|
||||
|
||||
После определения ответственности и публичной границы модуль может организовать реализацию [сегментами](./segments.md), компонентами и вложенными модулями. SLM не требует полного каркаса и не задаёт обязательный набор внутренних папок.
|
||||
|
||||
Каждый модуль физически размещается в отдельной папке. Это делает логическую границу наблюдаемой для потребителей и автоматических проверок, но не заменяет решение о владельце.
|
||||
|
||||
Наличие `index.ts` не используется как единственный признак модуля. Модульную границу определяют ответственность, владелец, публичные потребители и сопоставление путей, принятое проектом.
|
||||
|
||||
## Связанные правила
|
||||
|
||||
- [`SLM-MODULE-A004`](../rules/registry.md#slm-module-a004)
|
||||
- [`SLM-MODULE-A014`](../rules/registry.md#slm-module-a014)
|
||||
- [`SLM-MODULE-R006`](../rules/registry.md#slm-module-r006)
|
||||
- [`SLM-MODULE-R011`](../rules/registry.md#slm-module-r011)
|
||||
- [`SLM-MODULE-R012`](../rules/registry.md#slm-module-r012)
|
||||
- [`SLM-DEPENDENCY-A005`](../rules/registry.md#slm-dependency-a005)
|
||||
- [`SLM-COMPONENT-R009`](../rules/registry.md#slm-component-r009)
|
||||
- [`SLM-NESTED_MODULE-A010`](../rules/registry.md#slm-nested_module-a010)
|
||||
- [`SLM-LIFECYCLE-R013`](../rules/registry.md#slm-lifecycle-r013)
|
||||
- [`SLM-ENVIRONMENT-R016`](../rules/registry.md#slm-environment-r016)
|
||||
- [`SLM-ENVIRONMENT-R017`](../rules/registry.md#slm-environment-r017)
|
||||
- [`SLM-ENVIRONMENT-R018`](../rules/registry.md#slm-environment-r018)
|
||||
- [`SLM-ENVIRONMENT-R019`](../rules/registry.md#slm-environment-r019)
|
||||
92
docs/architecture/segments.md
Normal file
92
docs/architecture/segments.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Сегменты
|
||||
|
||||
Сегмент организует внутреннее содержимое одного модуля по назначению. Он помогает ориентироваться в реализации владельца, но не создаёт новую ответственность или архитектурную границу.
|
||||
|
||||
## Место в модели
|
||||
|
||||
Сегмент появляется только внутри уже определённого модуля:
|
||||
|
||||
```text
|
||||
Слой → [Группа*] → Модуль → [Сегмент*]
|
||||
```
|
||||
|
||||
Группа классифицирует несколько модулей внутри слоя. Сегмент классифицирует код одного модуля. Ни группа, ни сегмент не являются владельцами.
|
||||
|
||||
Все файлы, компоненты, состояние, зависимости и код жизненного цикла сегмента принадлежат родительскому модулю. Исключением является только вложенный модуль, который начинает собственную границу владения.
|
||||
|
||||
## Назначение
|
||||
|
||||
Сегмент используется, когда группировка внутренних файлов по назначению упрощает навигацию. Набор и названия сегментов определяет проект.
|
||||
|
||||
Возможная структура:
|
||||
|
||||
```text
|
||||
profile/
|
||||
├── index.ts
|
||||
├── profile.tsx
|
||||
├── hooks/ # Возможный сегмент
|
||||
├── services/ # Возможный сегмент
|
||||
├── stores/ # Возможный сегмент
|
||||
├── types/ # Возможный сегмент
|
||||
└── ui/ # Возможный сегмент
|
||||
```
|
||||
|
||||
Ни один из показанных сегментов не обязателен. Маленький модуль может хранить реализацию в корне без дополнительных каталогов.
|
||||
|
||||
Сегмент:
|
||||
|
||||
- не имеет самостоятельной ответственности;
|
||||
- не предоставляет публичный API;
|
||||
- не владеет состоянием или жизненным циклом;
|
||||
- не является узлом графа зависимостей;
|
||||
- не импортируется внешним кодом как отдельная архитектурная сущность.
|
||||
|
||||
Локальный `index.ts` может использоваться во внутренней единице сегмента, например в каталоге компонента. Он не превращает эту единицу или сегмент в модульную границу.
|
||||
|
||||
## Компоненты и вложенные модули
|
||||
|
||||
Сегмент может содержать компоненты и вспомогательные файлы родительского модуля. Компонент вправе иметь локальные `styles/`, `types/`, `tests/` и внутренний `index.ts`; всё это остаётся реализацией ближайшего модуля.
|
||||
|
||||
```text
|
||||
header/ # Модуль
|
||||
└── components/ # Сегмент
|
||||
└── button-submit/ # Компонент
|
||||
├── button-submit.tsx
|
||||
├── styles/
|
||||
│ └── button-submit.module.css
|
||||
├── types/
|
||||
│ └── button-submit.types.ts
|
||||
└── index.ts # Внутренняя точка входа
|
||||
```
|
||||
|
||||
Внешний код не импортирует `components/button-submit`. Если компонент нужен снаружи, модуль-владелец реэкспортирует его через собственный публичный фасет.
|
||||
|
||||
Сегмент также может содержать вложенные модули. В отличие от остальных файлов сегмента, каждый вложенный модуль сам владеет отдельной ответственностью и имеет публичный API и границу зависимостей.
|
||||
|
||||
```text
|
||||
landing/ # Родительский модуль
|
||||
└── parts/ # Сегмент
|
||||
└── hero/ # Вложенный модуль
|
||||
├── hero.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Имя `parts` является примером, а не обязательным соглашением SLM.
|
||||
|
||||
## Выбор границы
|
||||
|
||||
| Ситуация | Решение |
|
||||
|---|---|
|
||||
| Код относится к существующему владельцу и группируется только по назначению | Сегмент |
|
||||
| Части нужна собственная ответственность, API, зависимости, состояние или область жизни | Модуль |
|
||||
| Несколько модулей слоя нужно классифицировать для навигации | Группа |
|
||||
| Нескольким файлам не нужна отдельная группировка | Оставить в корне модуля |
|
||||
|
||||
Размер каталога и количество файлов не определяют выбор между сегментом и модулем.
|
||||
|
||||
## Связанные правила
|
||||
|
||||
- [`SLM-SEGMENT-R008`](../rules/registry.md#slm-segment-r008)
|
||||
- [`SLM-MODULE-A004`](../rules/registry.md#slm-module-a004)
|
||||
- [`SLM-COMPONENT-R009`](../rules/registry.md#slm-component-r009)
|
||||
- [`SLM-NESTED_MODULE-A010`](../rules/registry.md#slm-nested_module-a010)
|
||||
95
docs/reference/terminology.md
Normal file
95
docs/reference/terminology.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Терминология SLM
|
||||
|
||||
Этот документ задаёт нормативный смысл терминов. Определения используются при толковании архитектуры и правил, но сами по себе не являются отдельными правилами.
|
||||
|
||||
## Владение
|
||||
|
||||
### SLM root
|
||||
|
||||
Граница структурной архитектуры одного приложения. Внутри неё определяются владельцы ответственностей, слои, модули и их зависимости.
|
||||
|
||||
### Ответственность
|
||||
|
||||
Связная часть приложения с одной причиной изменяться. Ответственность является самостоятельной, когда ей нужны собственный публичный API, зависимости, состояние или область жизни.
|
||||
|
||||
### Владелец
|
||||
|
||||
Модуль, который определяет публичный API ответственности, её зависимости, состояние, область жизни и внутреннюю реализацию. Место выполнения кода не переносит владение.
|
||||
|
||||
## Структурные сущности
|
||||
|
||||
### Слой
|
||||
|
||||
Архитектурная роль кода внутри SLM root. Слой классифицирует владельцев по назначению и ограничивает допустимые направления зависимостей. Нормативные роли и матрица определены в разделе [Слои](../architecture/layers.md).
|
||||
|
||||
### Группа
|
||||
|
||||
Необязательный навигационный классификатор модулей внутри одного слоя или другой группы. Группа не является владельцем, публичным API или границей зависимостей.
|
||||
|
||||
### Модуль
|
||||
|
||||
Минимальная самостоятельная архитектурная единица SLM. Модуль владеет одной связной ответственностью, имеет публичный API и физически размещается в отдельной папке.
|
||||
|
||||
### Сегмент
|
||||
|
||||
Необязательная внутренняя часть одного модуля, группирующая его содержимое по назначению. Сегмент не является владельцем, публичным API или границей зависимостей.
|
||||
|
||||
### Компонент
|
||||
|
||||
Сущность фреймворка, реализующая часть интерфейса родительского модуля. Зависимости, состояние и жизненный цикл компонента принадлежат этому модулю и сами по себе не создают нового владельца. Компонент может иметь внутренний `index.ts`, который не является публичным фасетом SLM.
|
||||
|
||||
### Вложенный модуль
|
||||
|
||||
Обычный модуль, физически размещённый внутри родительского модуля. Он сам владеет отдельной ответственностью и имеет публичный API и границу зависимостей, но остаётся внутренней реализацией родителя для внешнего кода.
|
||||
|
||||
## Публичная граница
|
||||
|
||||
### Публичный API
|
||||
|
||||
Единый логический контракт внешнего доступа к модулю. Он скрывает внутреннюю реализацию и физически представлен обязательным фасетом `index` и только необходимыми фасетами `client`, `browser` и `server`.
|
||||
|
||||
### Фасет
|
||||
|
||||
Объявленная публичная точка входа модуля, открывающая часть его единого API для определённой среды выполнения. Импорт фасета не является глубоким импортом; любой другой внешний путь внутрь модуля остаётся внутренним.
|
||||
|
||||
### Глубокий импорт
|
||||
|
||||
Импорт или реэкспорт внутреннего пути чужого модуля, который не объявлен его публичным фасетом.
|
||||
|
||||
## Зависимости
|
||||
|
||||
### Зависимость
|
||||
|
||||
Направленная статическая связь между архитектурными границами внутри одного SLM root. Обычный импорт, импорт типа (`import type`) и реэкспорт одинаково создают архитектурную зависимость.
|
||||
|
||||
Зависимость внутреннего файла, сегмента или компонента относится к ближайшему модулю-владельцу. Вложенный модуль начинает собственную границу зависимостей.
|
||||
|
||||
### Нормативная матрица слоёв
|
||||
|
||||
Отношение допустимой зависимости между слоями одного SLM root. Матрица определяет доступные целевые роли, но не требует проходить через каждый промежуточный слой.
|
||||
|
||||
## Жизненный цикл
|
||||
|
||||
### Область жизни
|
||||
|
||||
Период, в течение которого принадлежащие модулю состояние или долгоживущий ресурс должны оставаться активными.
|
||||
|
||||
### Ресурс жизненного цикла
|
||||
|
||||
Ресурс, работа которого продолжается после первоначального вызова и требует остановки, отмены, отписки или освобождения. Например, подписка, обработчик событий, таймер, наблюдатель, запрос или соединение.
|
||||
|
||||
### Очистка
|
||||
|
||||
Гарантированное прекращение работы ресурса не позже завершения его области жизни. Автоматическая очистка фреймворка считается очисткой владельца, если модуль устанавливает и контролирует соответствующую границу.
|
||||
|
||||
## Немодульные единицы
|
||||
|
||||
### Точка входа фреймворка
|
||||
|
||||
Специальная немодульная единица слоя `app`, которая запускает приложение, объявляет точку маршрута, преобразует внешние входные данные или подключает готовые публичные API.
|
||||
|
||||
### Ресурс shared
|
||||
|
||||
Небольшая детерминированная единица слоя `shared`, не зависящая от продукта и не скрывающая отдельного внутреннего устройства. У неё нет изменяемого состояния, ввода-вывода, области жизни или собственного публичного API.
|
||||
|
||||
Путь и имя сами по себе не определяют ни одну из перечисленных сущностей. Физическое сопоставление задаётся стайлгайдом или конфигурацией проверки после определения ответственности и владельца.
|
||||
98
docs/reference/validation.md
Normal file
98
docs/reference/validation.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Проверка архитектуры
|
||||
|
||||
Проверка SLM подтверждает две разные стороны решения:
|
||||
|
||||
- смысловая проверка устанавливает ответственность, владельца и корректность границ;
|
||||
- структурная проверка подтверждает, что решение правильно выражено путями, публичными фасетами и зависимостями.
|
||||
|
||||
Успешная сборка или корректно отображаемый интерфейс не доказывают архитектурную корректность.
|
||||
|
||||
## Карточка решения
|
||||
|
||||
Перед изменением структуры нужно ответить:
|
||||
|
||||
| Вопрос | Что зафиксировать |
|
||||
|---|---|
|
||||
| Ответственность | Какой результат или поведение изменяется как единое целое |
|
||||
| Владелец | Какой модуль определяет контракт и внутреннюю реализацию |
|
||||
| Слой | Какой архитектурной роли соответствует ответственность |
|
||||
| Потребители | Кому действительно нужен публичный API |
|
||||
| Зависимости | Какие другие владельцы и возможности необходимы |
|
||||
| Состояние | Кто определяет смысл и допустимые изменения данных |
|
||||
| Жизненный цикл | Кто создаёт ресурсы, какова их область жизни и очистка |
|
||||
| Физическая форма | Какими путями и фасетами представлено принятое решение |
|
||||
|
||||
Если ответственность или владелец не определены, проверка путей откладывается: одинаковая файловая структура может представлять разные архитектурные решения.
|
||||
|
||||
## Архитектурное ревью
|
||||
|
||||
На ревью проверяется смысл кода, который нельзя надёжно вывести из файловой системы:
|
||||
|
||||
- одна ли связная ответственность находится внутри модуля;
|
||||
- есть ли у каждой самостоятельной ответственности ровно один владелец;
|
||||
- соответствует ли ответственность роли выбранного слоя;
|
||||
- не стали ли группа, сегмент или компонент скрытыми владельцами;
|
||||
- нужен ли каждый экспорт реальному внешнему потребителю;
|
||||
- не раскрывает ли публичный API изменяемые внутренние механизмы;
|
||||
- определены ли владелец, область жизни, число экземпляров и очистка каждого ресурса;
|
||||
- не переносится ли владение из-за места вызова, провайдера фреймворка или точки маршрута.
|
||||
|
||||
Окончательные смысловые требования имеют класс `R` в [реестре правил](../rules/registry.md).
|
||||
|
||||
## Автоматическая проверка
|
||||
|
||||
Проект сопоставляет физические пути с SLM root, слоями, группами, модулями, вложенными модулями, сегментами, фасетами, точками входа `app` и ресурсами `shared`. Сопоставление задаётся локальной конфигурацией и не меняет смысл сущностей.
|
||||
|
||||
Наличие `index.ts` само по себе не объявляет модуль. Проверка отличает объявленные корни модулей и их публичные фасеты от локальных точек входа компонентов и других внутренних единиц.
|
||||
|
||||
Автоматически проверяются:
|
||||
|
||||
- допустимое направление импортов по матрице слоёв;
|
||||
- отдельная папка каждого модуля;
|
||||
- доступ к чужому модулю только через объявленные фасеты;
|
||||
- отсутствие циклов между модулями;
|
||||
- отсутствие прямого внешнего доступа к вложенным модулям;
|
||||
- допустимый транзитивный граф исполняемых импортов каждого фасета среды выполнения;
|
||||
- динамическое подключение `browser`-фасета с отключённым SSR.
|
||||
|
||||
Каждое правило класса `A` должно полностью блокировать проверку при нарушении. SLM не требует конкретного lint-инструмента.
|
||||
|
||||
## Проверка зависимостей
|
||||
|
||||
Для каждого внешнего импорта определяется:
|
||||
|
||||
1. Модуль-владелец исходного файла.
|
||||
2. Модуль-владелец целевого файла.
|
||||
3. Слои исходного и целевого владельцев.
|
||||
4. Публичный фасет, через который выполнен импорт.
|
||||
5. Отсутствие цикла после добавления связи.
|
||||
|
||||
Импорты типов (`import type`) и реэкспорты проверяются как архитектурные связи. Относительные импорты внутри одного модуля не пересекают модульную границу.
|
||||
|
||||
## Проверка фасетов
|
||||
|
||||
Совместимость фасета определяется всем достижимым исполняемым кодом, а не только его собственным файлом.
|
||||
|
||||
Проверка подтверждает:
|
||||
|
||||
- `index` не достигает `client`, `browser` или `server`;
|
||||
- `client` не достигает `browser` или `server`;
|
||||
- `browser` и `server` не достигают друг друга;
|
||||
- `browser` доступен только через поддерживаемую динамическую границу без SSR;
|
||||
- специализированный фасет существует ради реального потребителя;
|
||||
- один исполняемый экспорт не дублируется между фасетами.
|
||||
|
||||
Импорт типа остаётся архитектурной зависимостью, но не добавляет исполняемый код в среду фасета.
|
||||
|
||||
## Критерий завершения
|
||||
|
||||
Изменение соответствует SLM, когда одновременно выполнены условия:
|
||||
|
||||
- ответственность и единственный владелец определены;
|
||||
- роль слоя соответствует ответственности;
|
||||
- публичный API минимален и используется всеми внешними потребителями;
|
||||
- зависимости разрешены и не образуют циклов;
|
||||
- группа и сегменты не подменяют модульную границу;
|
||||
- состояние и ресурсы имеют владельца и корректную область жизни;
|
||||
- физическая структура однозначно выражает принятое решение;
|
||||
- применимые автоматические проверки и архитектурное ревью пройдены.
|
||||
62
docs/rules/README.md
Normal file
62
docs/rules/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Правила SLM
|
||||
|
||||
Правило SLM задаёт один блокирующий архитектурный инвариант. Точные формулировки правил находятся только в [едином реестре](./registry.md); архитектурные главы объясняют модель и ссылаются на соответствующие коды.
|
||||
|
||||
## Виды утверждений
|
||||
|
||||
- **Определение** задаёт нормативный смысл термина.
|
||||
- **Правило** задаёт блокирующее требование.
|
||||
- **Рекомендация** помогает принять решение, но не является обязательной.
|
||||
- **Пример** показывает один из вариантов реализации и не задаёт каркас проекта.
|
||||
|
||||
Определения собраны в [терминологии](../reference/terminology.md). Определение может быть обязательным для толкования правил, но не получает отдельный код.
|
||||
|
||||
## Код правила
|
||||
|
||||
```text
|
||||
SLM-{group}-{class}{number}
|
||||
```
|
||||
|
||||
| Часть | Значение |
|
||||
|---|---|
|
||||
| `SLM` | Принадлежность архитектуре SLM |
|
||||
| `group` | Предмет правила |
|
||||
| `class` | Способ окончательной проверки: `A` или `R` |
|
||||
| `number` | Глобально уникальный трёхзначный номер |
|
||||
|
||||
Пример: [`SLM-MODULE-A004`](./registry.md#slm-module-a004).
|
||||
|
||||
## Способ проверки
|
||||
|
||||
### Автоматические правила (`A`)
|
||||
|
||||
Всё требование можно однозначно проверить программно по структуре проекта, публичным путям и графу импортов. Нарушение блокирует автоматическую проверку.
|
||||
|
||||
### Правила для ревью (`R`)
|
||||
|
||||
Для окончательного решения требуется понимание ответственности, владельца, потребителей или области жизни. Инструмент может найти подозрительный код, но не заменяет архитектурное решение.
|
||||
|
||||
## Разделы правил
|
||||
|
||||
| Код | Предмет |
|
||||
|---|---|
|
||||
| `LAYER` | Роль слоя и направление зависимостей |
|
||||
| `MODULE` | Ответственность, владение и публичная граница модуля |
|
||||
| `DEPENDENCY` | Граф зависимостей модулей |
|
||||
| `GROUP` | Навигационная группировка модулей |
|
||||
| `SEGMENT` | Внутренняя организация модуля |
|
||||
| `COMPONENT` | Принадлежность компонента модулю |
|
||||
| `NESTED_MODULE` | Доступ к вложенному модулю |
|
||||
| `LIFECYCLE` | Владение долгоживущими ресурсами |
|
||||
| `ENVIRONMENT` | Совместимость публичных фасетов со средами выполнения |
|
||||
|
||||
Раздел правила не создаёт одноимённую главу или дополнительный уровень архитектуры. Например, `GROUP` классифицирует правило о группировке, а сама группа остаётся необязательной частью слоя.
|
||||
|
||||
## Требования к реестру
|
||||
|
||||
- Одно правило защищает один инвариант.
|
||||
- Точная формулировка не повторяется в тематических документах.
|
||||
- Название кратко обозначает предмет, а описание полностью формулирует требование.
|
||||
- Рекомендации, обоснования и примеры не входят в формулировку правила.
|
||||
- Один инвариант не получает отдельные автоматическую и ручную копии.
|
||||
- Номер правила не обозначает важность и не переиспользуется после удаления.
|
||||
129
docs/rules/registry.md
Normal file
129
docs/rules/registry.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Реестр правил SLM
|
||||
|
||||
Здесь собраны правила SLM. Это единственное место, где они формулируются; тематические документы объясняют архитектуру и ссылаются на коды.
|
||||
|
||||
## Размещение кода по слоям
|
||||
|
||||
### SLM-LAYER-R001
|
||||
|
||||
> **Назначение слоёв**
|
||||
>
|
||||
> Код внутри SLM root размещается в слое, нормативная роль которого соответствует ответственности этого кода.
|
||||
|
||||
### SLM-LAYER-A002
|
||||
|
||||
> **Направление зависимостей**
|
||||
>
|
||||
> Внутри одного SLM root код каждого слоя может зависеть только от кода целевых слоёв, разрешённых для него нормативной матрицей слоёв.
|
||||
|
||||
### SLM-LAYER-R003
|
||||
|
||||
> **Граница слоя `app`**
|
||||
>
|
||||
> В `app` размещаются только точки входа фреймворка для запуска, маршрутов, преобразования входных данных и подключения публичных API модулей разрешённых слоёв или ресурсов `shared`; ответственности этих модулей остаются за пределами `app`.
|
||||
|
||||
## Границы модулей
|
||||
|
||||
### SLM-MODULE-A004
|
||||
|
||||
> **Публичный API модуля**
|
||||
>
|
||||
> Каждый модуль предоставляет единый логический публичный API через обязательный корневой фасет `index` и, при необходимости, фасеты `client`, `browser` и `server`; код за пределами модуля импортирует его содержимое только через эти фасеты.
|
||||
|
||||
### SLM-MODULE-A014
|
||||
|
||||
> **Папка модуля**
|
||||
>
|
||||
> Каждый модуль размещается в отдельной папке; его публичный API и внутренняя реализация находятся внутри этой границы, а вложенные модули образуют собственные папки.
|
||||
|
||||
### SLM-MODULE-R006
|
||||
|
||||
> **Ответственность модуля**
|
||||
>
|
||||
> Одна модульная граница содержит код одной связной ответственности; части, которые изменяются по несвязанным причинам, размещаются в разных модулях.
|
||||
|
||||
### SLM-MODULE-R011
|
||||
|
||||
> **Владелец ответственности**
|
||||
>
|
||||
> Каждая самостоятельная ответственность и относящийся к ней код принадлежат ровно одному модулю; вне модульной границы допускаются только точки входа `app` и нормативные ресурсы `shared`.
|
||||
|
||||
### SLM-MODULE-R012
|
||||
|
||||
> **Состав публичного API**
|
||||
>
|
||||
> Публичный API модуля открывает только контракт, необходимый реальным внешним потребителям; детали реализации и изменяемые внутренние механизмы остаются закрытыми.
|
||||
|
||||
## Зависимости между модулями
|
||||
|
||||
### SLM-DEPENDENCY-A005
|
||||
|
||||
> **Циклические зависимости**
|
||||
>
|
||||
> Зависимости между модулями внутри одного SLM root, включая вложенные модули, не образуют циклов.
|
||||
|
||||
## Назначение групп
|
||||
|
||||
### SLM-GROUP-R007
|
||||
|
||||
> **Назначение группы**
|
||||
>
|
||||
> Группа содержит только модули и другие группы, не владеет файлами реализации, состоянием, жизненным циклом или публичным API и не импортируется внешним кодом.
|
||||
|
||||
## Назначение сегментов
|
||||
|
||||
### SLM-SEGMENT-R008
|
||||
|
||||
> **Граница сегмента**
|
||||
>
|
||||
> Сегмент организует код только внутри одного модуля и не имеет собственной ответственности, публичного API или границы зависимостей.
|
||||
|
||||
## Ответственность компонентов
|
||||
|
||||
### SLM-COMPONENT-R009
|
||||
|
||||
> **Ответственность компонента**
|
||||
>
|
||||
> Компонент реализует часть ответственности одного родительского модуля; все его зависимости, состояние и жизненный цикл принадлежат этому модулю и не образуют самостоятельную архитектурную границу.
|
||||
|
||||
## Границы вложенных модулей
|
||||
|
||||
### SLM-NESTED_MODULE-A010
|
||||
|
||||
> **Доступ к вложенному модулю**
|
||||
>
|
||||
> Код за пределами родительского модуля не импортирует вложенный модуль напрямую и получает его экспорты только через публичный API родителя.
|
||||
|
||||
## Жизненный цикл
|
||||
|
||||
### SLM-LIFECYCLE-R013
|
||||
|
||||
> **Жизненный цикл ресурсов**
|
||||
>
|
||||
> Для каждого ресурса жизненного цикла модуль-владелец определяет создание, область жизни, число экземпляров и очистку; ресурс активен только внутри своей области жизни.
|
||||
|
||||
## Границы сред выполнения
|
||||
|
||||
### SLM-ENVIRONMENT-R016
|
||||
|
||||
> **Универсальный фасет**
|
||||
>
|
||||
> Корневой фасет `index` экспортирует только публичный код, совместимый как с серверным рендерингом, включая RSC, так и с клиентским выполнением, и не импортирует или реэкспортирует код фасетов `client`, `browser` или `server` прямо либо транзитивно.
|
||||
|
||||
### SLM-ENVIRONMENT-R017
|
||||
|
||||
> **Клиентский фасет**
|
||||
>
|
||||
> Фасет `client` экспортирует только клиентский код, который не может выполняться как RSC, и не импортирует или реэкспортирует код фасетов `browser` или `server` прямо либо транзитивно.
|
||||
|
||||
### SLM-ENVIRONMENT-R018
|
||||
|
||||
> **Браузерный фасет**
|
||||
>
|
||||
> Фасет `browser` экспортирует только browser-only код, а потребители импортируют его только динамически с отключённым SSR.
|
||||
|
||||
### SLM-ENVIRONMENT-R019
|
||||
|
||||
> **Серверный фасет**
|
||||
>
|
||||
> Фасет `server` экспортирует только server-only код и не импортируется или реэкспортируется фасетами `index`, `client` или `browser` прямо либо транзитивно.
|
||||
Reference in New Issue
Block a user