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

Архитектура SLM

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

Владение как основа

Ответственность — результат или поведение приложения, за которое отвечает один модуль-владелец. Она становится самостоятельной, когда ей нужны собственный публичный контракт, зависимости, состояние или область жизни, а не только внутренняя роль в работе другого модуля. Props, импорты, локальное состояние и lifecycle-код сами по себе этого не доказывают.

У каждой самостоятельной ответственности есть ровно один владелец. В SLM владельцем является модуль. Он определяет:

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

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

Доменный сценарий всегда получает владельца в слое domains. Его бизнес-правила, продуктовое состояние, операции с предметными данными, доменный UI и обслуживающие framework-механизмы остаются внутри доменной границы. Модуль compositions использует и компонует готовый публичный API домена, но не реализует сценарий вместо него. Если подходящего доменного модуля ещё нет, его отсутствие не является основанием временно разместить сценарий в композиции. Эта граница закреплена правилом SLM-DOMAIN-R022 и подробно описана в разделе Домены.

Структурная модель

text
SLM root
└── слой
    ├── модуль
    │   ├── сегмент
    │   └── вложенный модуль
    │       ├── сегмент
    │       └── вложенный модуль
    └── группа
        ├── модуль
        └── группа
            └── модуль
СущностьНазначениеВладеет ответственностью
СлойКлассифицирует код по архитектурной ролиНет
ГруппаНавигационно классифицирует модули внутри слояНет
МодульВладеет одной самостоятельной ответственностьюДа
ДоменСпециализирует модуль для предметной ответственности, контракта и ошибокДа, как модуль
СегментОрганизует внутренности одного модуляНет

Домен не добавляет уровень в структурное дерево: в слое domains он занимает место обычного модуля и отличается дополнительными инвариантами.

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

Слой и группа находятся снаружи модульной границы. Сегмент находится внутри неё. Framework-компоненты, Providers, Guards, hooks, stores, services и другой внутренний код не являются архитектурными сущностями SLM и принадлежат ближайшему модулю.

Внутренняя реализация

Модуль может содержать любой код, относящийся к его ответственности. SLM ограничивает не набор framework-механизмов, а владение и наблюдаемую структуру:

  • помимо опционального главного framework-файла в корне, остальные компонентные единицы располагаются на одном внутреннем уровне относительно модуля;
  • каталог компонентной единицы не содержит другие компонентные единицы или вложенные модули;
  • рекурсивная структурная вложенность создаётся только вложенными модулями;
  • помимо публичных фасетов, в корне находится не более одного главного implementation- или assembly-файла;
  • если главный файл нельзя определить уверенно, реализация размещается в сегментах.

Ограничение глубины framework-компонентов относится к файловой организации, а не к runtime-дереву фреймворка.

Порядок проектирования

Архитектурное решение принимается от смысла к структуре:

  1. Описать результат, который должен получить пользователь или приложение.
  2. Назначить модуль, который отвечает за этот результат.
  3. Определить, что модуль делает сам, а что получает от других модулей.
  4. Определить, кто использует результат работы модуля.
  5. Выбрать слой по роли ответственности.
  6. Спроектировать публичный API и допустимые зависимости.
  7. При необходимости классифицировать модули группами, организовать внутренний код сегментами и выбрать физические пути.

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

Например, Header сам определяет расположение шапки, отображение навигации и состояние мобильного меню. Текущего пользователя он получает от модуля auth, а Button и Avatar — от модулей ui. Если удалить Header, авторизация и UI-компоненты останутся нужны приложению, поэтому Header использует их, но не владеет ими. Header может сообщить через infra о показе собственной раскладки, но получение пользователя и события сценария авторизации остаются ответственностью auth.

Если ответственность или владелец не определены, файловая структура не может исправить архитектурную неопределённость.

Логическая и физическая границы

Модуль не определяется наличием папки, index.ts, framework-компонента или нескольких файлов. Его определяет самостоятельная ответственность и владение её контрактом, зависимостями, состоянием и жизненным циклом.

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

Верны обе формулировки:

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

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

Область применения

SLM применяется внутри SLM root — границы структурной архитектуры одного приложения. Это может быть src/ или другая область, установленная проектом.

Архитектура определяет:

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

SLM не задаёт обязательный поток данных, конкретный framework, фиксированные имена сегментов, правила монорепозиториев или полный файловый стайлгайд.

Нормативный смысл терминов находится в терминологии. Точные блокирующие требования объявлены только в реестре правил.

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