# Зависимости Зависимость связывает архитектурных владельцев. Исходный файл создаёт ребро от своего ближайшего модуля к ближайшему модулю импортируемого файла. ## Модульный граф Узлами архитектурного графа являются модули, включая вложенные. Группы, сегменты, 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`, предметный сценарий остаётся ответственностью доменного модуля, а техническая возможность — ответственностью инфраструктурного. ## Допустимость связи и владение Матрица отвечает только на вопрос, может ли один слой зависеть от другого. Она не разрешает исходному модулю реализовывать ответственность, которая по своей роли принадлежит целевому или другому слою. `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. Если эта связь задаёт обязательный порядок, продуктовые условия, общий предметный результат или политику ошибок между доменами, она является отдельным доменным сценарием, а не внутренней логикой композиции. ## Зависимости внутри слоя Модули одного слоя могут зависеть друг от друга в любом направлении при одновременном выполнении двух условий: 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) - [`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) - [`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)