ADR-UI-013. Паттерн страницы фичи
Страницы распадаются на повторяющиеся форматы (грид, карточка, форма) плюс специфичные экраны. Фиксируем ядро паттерна — per-page service и тонкий компонент; детали типовых форматов дописываются в этот ADR по мере появления соответствующих base-компонентов.
1. Контекст и постановка задачи
Если каждый собирает страницу по-своему — нет единообразия загрузки, состояния, ошибок. Нужен единый каркас страницы фичи: связка «страница ↔ её сервис», формат состояния, индикаторы загрузки, показ уведомлений.
На текущем этапе base-компоненты (таблица, модалка, дровер, тост) ещё не готовы, поэтому фиксируем ядро паттерна; детали типовых форматов дописываются сюда по мере их появления.
2. Драйверы решения
- Единообразие — одинаковые страницы ведут себя одинаково.
- Локальность — состояние и логика страницы живут рядом со страницей.
- Тестируемость — per-page service изолируется как обычный класс.
- Готовность к codegen — стабильные правила для будущих генераторов страниц.
3. Рассмотренные варианты
- A. Логика прямо в компоненте — мало файлов, но компонент разрастается, тесты тяжёлые.
- B. Per-page service в
providersстраницы + типовой каркас — состояние/API/эффекты в сервисе; компонент тонкий. - C. Отдельный store на страницу — требует библиотеки store, которой по ADR-UI-001 нет.
4. Решение
Выбран Вариант B.
4.1. Ядро (принято сейчас)
Per-page service — обязателен для нетривиальной страницы; регистрируется в providers
страницы:
- Жизненный цикл = жизнь страницы (создаётся при входе, уничтожается при выходе).
- Хранит всё, что нужно нескольким частям страницы, — через сигналы (ADR-UI-008).
- Делает вызовы API + маппинг + побочные эффекты (уведомления и т. п.).
- Не делает: не лезет в глобальное состояние без нужды; не публикует
Subject-ы другим страницам (между страницами — через роутер и общие сервисы). - Имя —
<Name>PageService. - Компонент страницы — тонкий: разметка + делегирование в сервис.
Тривиальная страница может обойтись без per-page service.
Индикаторы загрузки — сигналы (loading, submitting) в per-page service; шаблон
реагирует через @if.
Уведомления (тосты) — только через сервис-обёртку NotifyToast (единый стиль и место
конфигурации); прямые вызовы запрещены. Реализация сервиса — при появлении base-тоста.
4.2. Типовые форматы (концепция; детали — по мере появления base-компонентов)
- Grid (список) — каркас
icd-page+ таблица + пагинатор (загрузка/сортировка/фильтр). Детали — при появлении baseTableи утилиты пагинатора. - Card (чтение) —
<Name>PageServiceгрузит сущность в сигнал поidиз маршрута. - Create/Edit (форма) — форма на Signal Forms (ADR-UI-009); сервис делает
create/update; на submit — валидация → вызов → уведомление + редирект. Детали дровер-форм и удаления — при появлении baseModal/Drawer. - Специфичные экраны (мастер и т. п., напр. «Ввод данных») — вне типовых форматов, но с тем же per-page service для состояния.
4.3. Чего не делаем
- Не публикуем
Subjectper-page service наружу другим страницам. - Не вызываем уведомления/модалки напрямую — только через обёртки.
- Не делаем shared-фильтры между страницами — фильтр живёт в per-page service одной страницы.
5. Положительные следствия
- Любая страница собирается из понятных кубиков: page → page-service → формат.
- Тесты per-page service просты (чистый класс с инжектами).
- Задел под генераторы страниц.
6. Отрицательные следствия и компромиссы
- На простую страницу ложится оверхед (per-page service, каркас) — для тривиальных допускаем отказ.
- Часть паттерна (grid/модалки/тосты) пока не детализирована — дополняется по мере появления base-компонентов, до тех пор эти форматы собираются вручную.
7. Проверка
- Per-page services регистрируются в
providersстраницы, не вroot. - Компонент страницы — тонкий; состояние и вызовы API — в сервисе.
- Тосты — только через
NotifyToast; индикаторы загрузки — через сигналы сервиса.
8. Открытые вопросы / отложено (дополнить при появлении base-компонентов)
- Grid-формат — детали таблицы и пагинатора (загрузка/сортировка/фильтр, empty-state) —
при появлении base
Table. - Модалки/дроверы — формат create/edit в дровере и удаление через confirm-модалку — при
появлении base
Modal/Drawer. - Тосты — реализация сервиса
NotifyToast— при появлении base-тоста (overlay). - API/маппинг — связка per-page service ↔ API — отдельным ADR при появлении контракта API.
- Права — видимость действий (меню строки, кнопки) — при появлении auth/claims.