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

12 KiB
Raw Permalink Blame History

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

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

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

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

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

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

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

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

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

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, фиксированные имена сегментов, правила монорепозиториев или полный файловый стайлгайд.

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