ADR-UI-006. Маршрутизация
Приложение содержит несколько фич-страниц (ввод, очередь обработки, карты, документы, администрирование). Нужно зафиксировать, как декларируются маршруты, где живёт реестр имён сегментов (чтобы не было литералов URL в коде) и какова URL-структура. Механику защиты доступа (гарды) выносим в отдельный ADR — роли и доступ пока не определены.
1. Контекст и постановка задачи
Фич-страниц несколько, и число будет расти. Нужно зафиксировать:
- как декларируются маршруты (один файл против корневой + per-feature);
- где живёт реестр имён сегментов, чтобы избежать литералов URL в коде;
- какова URL-структура.
Защита маршрутов гардами (аутентификация, права) зависит от ролей и доступа, которые пока не
заданы (см. IcdVision), — выносится в отдельный ADR.
2. Драйверы решения
- Lazy loading — фичи подгружаются по требованию; стартовый bundle небольшой.
- Единый источник имён сегментов — нет литералов вроде
'/input'в коде; переименование URL — одна точка правки. - Плоская структура (ADR-UI-003) — URL не повторяет навигационную группировку меню.
- Декларативность — маршрут фичи объявляет сама фича.
3. Рассмотренные варианты
- A. Один файл
app.routes.tsсо всеми маршрутами — просто, но тащит код всех фич в initial bundle и плохо масштабируется. - B. Корневой
app.routes.ts+ per-feature<feature>.routes.ts, lazy-loaded — каждая фича объявляет свои маршруты. - C. Группировка маршрутов по разделам меню (
/admin/users) — лишняя вложенность в URL и файлах.
4. Решение
Выбран Вариант B: корневой app.routes.ts + per-feature маршруты, lazy-loaded.
4.1. Оболочка и стартовая страница
Оболочка приложения (шапка + рабочая область) — это корневой компонент App (ADR-UI-005),
не маршрут-обёртка: все страницы рендерятся в его router-outlet. Стартовый '' ведёт на
главную (HomePage) напрямую. Когда появится режим «без оболочки» (экраны входа) и защита
доступа — layout вынесется в маршрут-обёртку отдельным ADR.
4.2. Корневой src/app/app.routes.ts
export const routes: Routes = [
{ path: BaseRoutes.home, pathMatch: 'full',
loadComponent: () => import('@app/modules/home/pages/home/home').then((m) => m.Home) },
{ path: BaseRoutes.input,
loadChildren: () => import('@app/modules/input/input.routes').then((m) => m.inputRoutes) },
// ... queue / cards / documents / admin
{ path: '**', redirectTo: BaseRoutes.home },
];
4.3. Per-feature <feature>.routes.ts
Объявляет внутреннюю структуру фичи (список / создание / карточка). Внутренние сегменты — в реестре фичи, если их больше двух-трёх.
4.4. Реестр сегментов — src/app/services/router/
Единый источник имён; классы без префикса (ADR-UI-002):
export const BaseRoutes = {
root: '/',
home: '',
input: 'input',
queue: 'queue',
cards: 'cards',
documents: 'documents',
admin: 'admin',
} as const;
export const RouteParam = {
packetId: 'packetId',
cardId: 'cardId',
documentId: 'documentId',
} as const;
Внутренние сегменты фичи — в services/router/<feature>-routes.ts (например
InputRouteSegments = { list: 'list', new: 'new' }), когда их несколько.
4.5. URL-структура
- Плоская:
/input,/queue,/cards,/documents,/admin. - Без префикса групп навигации — группировка живёт только в меню (ADR-UI-005).
- Корневой
''→ главная.
4.6. Правила использования
- Никаких литералов URL в коде. Сборка пути — через
BaseRoutes/RouteParam(routerLink,router.navigate). - Внутренние сегменты фичи — в реестре фичи, не дублируются между маршрутами и компонентами.
- Lazy load обязателен для фич (несколько страниц или свои
services/models); одностраничные — черезloadComponent.
5. Положительные следствия
- Initial bundle — только оболочка и общие компоненты; фичи грузятся по требованию.
- Реестр сегментов — единая точка правки при переименовании путей.
- Маршрут фичи объявляет сама фича — структура не расползается.
6. Отрицательные следствия и компромиссы
- URL не отражает навигационную группировку — при необходимости (deep-links) пересмотрим.
- Реестр
BaseRoutesнужно держать в синхроне с реальными маршрутами — ревью / линт.
7. Проверка
- Новая фича добавляется тремя действиями: сегмент в
BaseRoutes→modules/<feature>/ <feature>.routes.ts→ подключение черезloadChildrenвapp.routes.ts. - В коде нет литералов вроде
'/input'—grepподтверждает.
8. Открытые вопросы / отложено
- Гарды доступа — аутентификация и права на маршрутах (защита оболочки, требуемые права на фиче, стартовый редирект по правам) — отдельным ADR при определении ролей/доступа.
- Внутренние сегменты фич — заводятся по мере появления многостраничных фич.
- Группировка URL по разделам — не делаем; при необходимости отдельный ADR.