Files
slm-design/docs/architecture/layers.md
S. Gromov 691069af8e sync
2026-08-10 09:12:22 +03:00

9.8 KiB
Raw Blame History

Слои

Слой классифицирует код по архитектурной роли и задаёт допустимые направления зависимостей. Слой не владеет ответственностью: владельцем остаётся модуль, размещённый в этом слое.

Слой выбирается после определения ответственности. Похожее имя папки или наличие зависимости от конкретной библиотеки не являются основанием для выбора слоя.

Роли слоёв

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) и реэкспорт одинаково участвуют в архитектурном графе. Для связи между модулями дополнительно действуют их публичные границы и запрет циклов.

Разрешённое направление не переносит владение. Если модуль domains использует infra, предметный сценарий остаётся ответственностью доменного модуля, а техническая возможность — ответственностью инфраструктурного.

infra может использовать ui, когда технической возможности нужно собственное визуальное представление: CAPTCHA, uploader, карта или инструмент разработчика. ui не использует infra; необходимые технические возможности универсальный UI получает через входной контракт.

Группировка модулей

Группа классифицирует модули внутри одного слоя или другой группы. Она нужна, когда плоский список модулей перестаёт быть понятным.

compositions/
├── pages/              # Группа
│   ├── catalog/        # Модуль
│   └── profile/        # Модуль
├── layouts/            # Группа
│   └── main/           # Модуль
└── widgets/            # Группа
    └── cart-summary/   # Модуль

Группа:

  • содержит только модули и вложенные группы;
  • не владеет ответственностью или реализацией;
  • не имеет состояния и жизненного цикла;
  • не предоставляет публичный API;
  • не является узлом графа зависимостей;
  • не реэкспортирует содержащиеся в ней модули.

Модуль может находиться непосредственно в слое. Группа вводится только ради реальной классификации, а её названия и глубину определяет проект.

Группа организует несколько владельцев внутри слоя. Сегмент организует код внутри одного владельца.

Немодульные исключения

Внутри SLM root код по умолчанию принадлежит модулю. Исключения ограничены двумя случаями:

  • точка входа app непосредственно связывает приложение с фреймворком;
  • ресурс shared является небольшой самостоятельной детерминированной единицей без внутренней границы.

Если ресурсу shared нужны несколько файлов реализации, собственные архитектурные зависимости, изменяемое состояние, ввод-вывод или жизненный цикл, ему требуется модуль-владелец.

Связанные правила