Files
slm-design/docs/architecture/dependencies.md

139 lines
11 KiB
Markdown
Raw Permalink Normal View History

2026-08-10 12:37:32 +03:00
# Зависимости
Зависимость связывает архитектурных владельцев. Исходный файл создаёт ребро от своего ближайшего модуля к ближайшему модулю импортируемого файла.
## Модульный граф
Узлами архитектурного графа являются модули, включая вложенные. Группы, сегменты, framework-компоненты, hooks, stores и другие файлы реализации отдельных узлов не создают.
Для каждой связи определяются:
1. Ближайший модуль-владелец исходного файла.
2. Ближайший модуль-владелец целевого файла.
3. Слои исходного и целевого владельцев.
4. Публичный фасет, через который пересечена граница.
Обычный импорт, `import type` и реэкспорт одинаково создают архитектурное ребро. Связь файлов внутри одного модуля остаётся внутренней реализацией и не создаёт межмодульную зависимость.
Вложенный модуль начинает новый узел. Импорт из родительского модуля во вложенный или обратно проверяется как обычная межмодульная связь.
## Публичная граница
При пересечении модульной границы используется только объявленный публичный фасет целевого модуля:
```ts
// Допустимо
import { Button } from '@/ui/button'
// Недопустимый глубокий импорт
import { Button } from '@/ui/button/button'
```
Разрешённое направление слоя или отсутствие цикла не делает глубокий импорт допустимым.
## Направление между слоями
Матрица определяет, от каких слоёв может зависеть исходный слой:
| Исходный слой | Допустимые целевые слои |
|---|---|
| `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`.
Разрешённое направление не переносит владение. Если модуль `domains` использует `infra`, предметный сценарий остаётся ответственностью доменного модуля, а техническая возможность — ответственностью инфраструктурного.
2026-08-10 14:42:29 +03:00
## Допустимость связи и владение
Матрица отвечает только на вопрос, может ли один слой зависеть от другого. Она не разрешает исходному модулю реализовывать ответственность, которая по своей роли принадлежит целевому или другому слою.
`compositions` может зависеть от `domains` и `infra`, но использует эти направления по-разному:
- доменный сценарий доступен композиции только как готовый публичный API доменного модуля;
- инфраструктурный API может обслуживать собственную техническую потребность композиции, например тему, локализацию или доставку метрики показа страницы;
- инфраструктурный HTTP-клиент, SDK или storage не используются композицией для реализации продуктовой операции, загрузки предметных данных или определения доменного исхода.
```ts
// Допустимо: композиция использует готовый доменный UI.
import { OrdersList } from '@/domains/orders/client'
// Допустимо: техническая возможность обслуживает саму композицию.
import { useTheme } from '@/infra/theme/client'
// Направление импорта допустимо, но ответственность выбрана неверно.
import { http } from '@/infra/http'
await http.get('/orders')
```
В последнем примере запрос получает предметные данные и участвует в доменном сценарии. Его смысл, параметры, продуктовые исходы и вызов принадлежат доменному модулю, который открывает композиции готовый API.
Разрешённая зависимость `domains` от `infra` также не объединяет их контракты. Домен может использовать HTTP-транспорт, SDK или storage через публичный API инфраструктурного модуля, но DTO и ошибки источника остаются только во внутреннем интеграционном коде домена. До попадания в правила, состояние, доменный UI или публичный результат данные адаптируются к доменному контракту, а ошибка источника интерпретируется в терминах текущего сценария. Полные требования описаны в разделе [Домены](./domains.md#граница-внешних-данных).
Аналогично, технической доставкой метрик владеет `infra`, но смысл события определяется модулем-владельцем наблюдаемого поведения. Разрешённый вызов telemetry API из композиции не позволяет ей объявлять события доменного сценария от своего имени.
Композиция может размещать и связывать несколько доменных API. Если эта связь задаёт обязательный порядок, продуктовые условия, общий предметный результат или политику ошибок между доменами, она является отдельным доменным сценарием, а не внутренней логикой композиции.
2026-08-10 12:37:32 +03:00
## Зависимости внутри слоя
Модули одного слоя могут зависеть друг от друга в любом направлении при одновременном выполнении двух условий:
1. Целевой модуль используется только через публичный API.
2. Общий модульный граф остаётся ацикличным.
Принадлежность модулей одной или разным [группам](./groups.md) не влияет на разрешение связи. Группа не имеет API и не является промежуточным узлом импорта.
SLM не задаёт отдельные same-layer матрицы для `pages`, `layouts`, `widgets`, доменов, инфраструктуры, UI или shared. Если проекту нужна более строгая локальная политика, она является дополнительным проектным ограничением, а не общим правилом SLM.
## Запрет циклов
Общий граф модулей внутри одного SLM root остаётся ацикличным. Запрет действует для модулей одного слоя, разных разрешённых слоёв и вложенных модулей.
Проверки только файлового графа недостаточно. Например:
```text
module-a/file-1.ts → module-b/file-1.ts
module-b/file-2.ts → module-a/file-2.ts
```
Между конкретными файлами может не существовать замкнутого пути, но после сопоставления файлов владельцам возникает архитектурный цикл:
```text
module-a ↔ module-b
```
Lint-проверка модульных циклов должна:
1. Сопоставить каждый файл ближайшему модулю-владельцу.
2. Свернуть внутренние импорты файлов одного модуля.
3. Добавить межмодульные рёбра для импортов типов, исполняемого кода и реэкспортов.
4. Считать каждый вложенный модуль отдельным узлом.
5. Блокировать любое сильносвязное множество из нескольких модулей.
Стандартная file-level проверка циклов может использоваться дополнительно, но не заменяет проверку модульного графа.
## Проверка связи
Для каждого нового или изменённого импорта проверяются три условия:
1. Направление разрешено матрицей слоёв.
2. Целевая модульная граница пересечена через публичный фасет.
3. После добавления ребра модульный граф остаётся ацикличным.
## Связанные правила
- [`SLM-LAYER-A002`](../rules/registry.md#slm-layer-a002)
2026-08-10 14:42:29 +03:00
- [`SLM-DOMAIN-R022`](../rules/registry.md#slm-domain-r022)
- [`SLM-DOMAIN-R023`](../rules/registry.md#slm-domain-r023)
- [`SLM-DOMAIN-R024`](../rules/registry.md#slm-domain-r024)
- [`SLM-DOMAIN-R025`](../rules/registry.md#slm-domain-r025)
- [`SLM-DOMAIN-R026`](../rules/registry.md#slm-domain-r026)
2026-08-10 12:37:32 +03:00
- [`SLM-MODULE-A004`](../rules/registry.md#slm-module-a004)
- [`SLM-DEPENDENCY-A005`](../rules/registry.md#slm-dependency-a005)
- [`SLM-NESTED_MODULE-A010`](../rules/registry.md#slm-nested_module-a010)