Перейти к содержанию

Слои

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

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

Роли слоёв

SLM определяет шесть ролей:

СлойРоль
appСвязь приложения с фреймворком: запуск, маршруты, преобразование внешних входных данных и подключение готовых публичных API
compositionsПредставление и связывание готовых публичных API в страницы, макеты, экраны, виджеты и другие продуктовые композиции
domainsПолная реализация предметных ответственностей и сценариев, включая их модели, правила, состояние, операции с продуктовыми данными и доменный UI
infraТехнические сервисы и возможности приложения без собственной предметной модели
uiУниверсальные интерфейсные модули без зависимости от конкретной продуктовой композиции
sharedДетерминированный фундамент без знания о продукте, изменяемого состояния и ввода-вывода

Отсутствующая роль не требует пустой папки. Проект создаёт слой только тогда, когда в нём появляется соответствующая ответственность.

App

app содержит точки связи с фреймворком: запуск, файлы маршрутов и преобразование внешних входных данных. Они подключают готовые публичные API других слоёв, но не присваивают их ответственность.

Точки входа app являются специальным немодульным исключением. Самостоятельная продуктовая ответственность, даже если она представлена страницей, макетом или Provider, реализуется в подходящем модуле и только подключается из app.

Compositions

compositions содержит владельцев представления продуктовых композиций: страниц, макетов, экранов, виджетов, результатов маршрутов и интерфейса, объединяющего несколько готовых модульных возможностей. Композиция размещает и связывает публичные API доменных, инфраструктурных и UI-модулей, но не присваивает их ответственность.

Модуль композиции может владеть структурой страницы, расположением частей интерфейса и состоянием, смысл которого существует только внутри этой композиции. Он не определяет, не реализует, не расширяет и не замещает доменный сценарий. Количество потребителей и использование сценария только на одной странице не меняют эту границу.

Конкретная организация слоя определяется продуктом и фреймворком. Названия pages, layouts, screens и widgets могут использоваться как группы, но не являются дополнительными слоями и не задают направление импортов.

Domains

domains содержит модули-владельцы полных предметных ответственностей и доменных сценариев. Доменный модуль владеет не только моделями и бизнес-правилами, но и продуктовым состоянием, смыслом операций с продуктовыми данными, предметными исходами, доменным UI и framework-механизмами, которые обслуживают сценарий.

Домен является вертикальным владельцем ответственности, а не только каталогом независимой от интерфейса бизнес-логики. Внутри него могут находиться компоненты, Providers, hooks, stores, services и другой код, если он реализует принадлежащий домену результат. Универсальные визуальные элементы домен получает из ui, а технические возможности без предметной модели — из infra.

Домен является специализированным SLM-модулем: он сохраняет обычную модульную форму и получает дополнительные требования к предметному контракту, адаптации источников и ошибкам. Полная модель описана в разделе Домены.

Infra

infra содержит технические возможности приложения: аналитику, локализацию, тему, телеметрию, интеграции с платформой и другие сервисы без собственной предметной модели.

Технический способ выполнения предметного сценария не переносит владение сценарием из domains в infra.

UI

ui содержит универсальные интерфейсные модули, которые не знают о конкретной странице, маршруте или продуктовой композиции.

Shared

shared содержит детерминированный фундамент, не зависящий от продукта и не имеющий ввода-вывода, изменяемого состояния или жизненного цикла.

В shared могут находиться обычные модули и небольшие немодульные ресурсы: чистые функции, общие типы, стили, декларативная конфигурация и статические файлы.

Граница доменов и композиций

Граница определяется смыслом поведения, а не местом его вызова или отображения:

Код определяетСлой-владелец
Продуктовую операцию, правило, переход, предметный исход или состояниеdomains
Получение или изменение продуктовых данных, параметры операции и смысл её ошибокdomains
Форму, список, карточку, Guard или другой UI, выраженный в терминах одного доменаdomains
Расположение и связывание готовых публичных API на странице или экранеcompositions
Состояние панели, секции или раскладки, имеющее смысл только в одной композицииcompositions
Универсальный визуальный элемент без предметного смыслаui
HTTP-транспорт, тему, локализацию или техническую доставку телеметрииinfra

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

Композиция может показать рядом несколько доменных возможностей и вызвать их готовые публичные операции. Если координация определяет продуктовый порядок действий, условия, общий предметный результат, политику ошибок или компенсацию между доменами, такая координация сама является доменным сценарием и требует владельца в domains.

Разрешённая зависимость compositions от infra сохраняется. Композиция может использовать тему, локализацию, доставку метрик и другие технические возможности для собственной ответственности. Эта связь не разрешает получать или изменять продуктовые данные через HTTP-клиент, SDK или storage непосредственно из композиции: техническим механизмом владеет infra, а смысл такой операции и её продуктовый результат принадлежат домену.

Смысл события метрики принадлежит владельцу наблюдаемого поведения. Домен определяет событие доменного сценария, композиция — событие показа или взаимодействия со своей раскладкой, app — событие запуска или маршрутизации, а infra отвечает за техническую доставку телеметрии.

Направление зависимостей

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

Нормативная матрица, правила same-layer импортов и требования к lint-проверке находятся в разделе Зависимости.

Группировка

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

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

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

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

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

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

Документация SLM Design