mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
121 lines
9.8 KiB
Markdown
121 lines
9.8 KiB
Markdown
|
|
# Слои
|
|||
|
|
|
|||
|
|
Слой классифицирует код по архитектурной роли и задаёт допустимые направления зависимостей. Слой не владеет ответственностью: владельцем остаётся модуль, размещённый в этом слое.
|
|||
|
|
|
|||
|
|
Слой выбирается после определения ответственности. Похожее имя папки или наличие зависимости от конкретной библиотеки не являются основанием для выбора слоя.
|
|||
|
|
|
|||
|
|
## Роли слоёв
|
|||
|
|
|
|||
|
|
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`) и реэкспорт одинаково участвуют в архитектурном графе. Для связи между модулями дополнительно действуют их [публичные границы и запрет циклов](./modules.md#зависимости-между-модулями).
|
|||
|
|
|
|||
|
|
Разрешённое направление не переносит владение. Если модуль `domains` использует `infra`, предметный сценарий остаётся ответственностью доменного модуля, а техническая возможность — ответственностью инфраструктурного.
|
|||
|
|
|
|||
|
|
`infra` может использовать `ui`, когда технической возможности нужно собственное визуальное представление: CAPTCHA, uploader, карта или инструмент разработчика. `ui` не использует `infra`; необходимые технические возможности универсальный UI получает через входной контракт.
|
|||
|
|
|
|||
|
|
## Группировка модулей
|
|||
|
|
|
|||
|
|
Группа классифицирует модули внутри одного слоя или другой группы. Она нужна, когда плоский список модулей перестаёт быть понятным.
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
compositions/
|
|||
|
|
├── pages/ # Группа
|
|||
|
|
│ ├── catalog/ # Модуль
|
|||
|
|
│ └── profile/ # Модуль
|
|||
|
|
├── layouts/ # Группа
|
|||
|
|
│ └── main/ # Модуль
|
|||
|
|
└── widgets/ # Группа
|
|||
|
|
└── cart-summary/ # Модуль
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Группа:
|
|||
|
|
|
|||
|
|
- содержит только модули и вложенные группы;
|
|||
|
|
- не владеет ответственностью или реализацией;
|
|||
|
|
- не имеет состояния и жизненного цикла;
|
|||
|
|
- не предоставляет публичный API;
|
|||
|
|
- не является узлом графа зависимостей;
|
|||
|
|
- не реэкспортирует содержащиеся в ней модули.
|
|||
|
|
|
|||
|
|
Модуль может находиться непосредственно в слое. Группа вводится только ради реальной классификации, а её названия и глубину определяет проект.
|
|||
|
|
|
|||
|
|
Группа организует несколько владельцев внутри слоя. [Сегмент](./segments.md) организует код внутри одного владельца.
|
|||
|
|
|
|||
|
|
## Немодульные исключения
|
|||
|
|
|
|||
|
|
Внутри SLM root код по умолчанию принадлежит модулю. Исключения ограничены двумя случаями:
|
|||
|
|
|
|||
|
|
- точка входа `app` непосредственно связывает приложение с фреймворком;
|
|||
|
|
- ресурс `shared` является небольшой самостоятельной детерминированной единицей без внутренней границы.
|
|||
|
|
|
|||
|
|
Если ресурсу `shared` нужны несколько файлов реализации, собственные архитектурные зависимости, изменяемое состояние, ввод-вывод или жизненный цикл, ему требуется модуль-владелец.
|
|||
|
|
|
|||
|
|
## Связанные правила
|
|||
|
|
|
|||
|
|
- [`SLM-LAYER-R001`](../rules/registry.md#slm-layer-r001)
|
|||
|
|
- [`SLM-LAYER-A002`](../rules/registry.md#slm-layer-a002)
|
|||
|
|
- [`SLM-LAYER-R003`](../rules/registry.md#slm-layer-r003)
|
|||
|
|
- [`SLM-GROUP-R007`](../rules/registry.md#slm-group-r007)
|
|||
|
|
- [`SLM-MODULE-R011`](../rules/registry.md#slm-module-r011)
|