⟨/⟩ 40_Arch/Tech/Angular/ADR_UI_006_Routing.cs

176 строк · в начало

using System;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd;

namespace Ban.Sdaid.Icd.Arch.Tech.Angular
{
    /// <summary>
    /// ADR-UI-006: Маршрутизация. Корневой + per-feature lazy-маршруты, единый реестр
    /// сегментов (без литералов URL), плоская URL-структура. Гарды доступа — отдельным ADR.
    /// </summary>
    public class ADR_UI_006_Routing : IAdrDocument, IFolder<ArchAngularFolder>
    {
        public string Name => "ADR-UI-006. Маршрутизация";

        public string Description =>
            @"Приложение содержит несколько фич-страниц (ввод, очередь обработки, карты, документы,
администрирование). Нужно зафиксировать, как декларируются маршруты, где живёт реестр имён сегментов
(чтобы не было литералов URL в коде) и какова URL-структура. Механику защиты доступа (гарды) выносим
в отдельный ADR — роли и доступ пока не определены.";

        public string Version => "1.0";
        public string Status => "accepted";
        public string[] Comments => new[]
        {
            "2026-08-11. Принято перед реализацией страниц; гарды доступа отложены до определения ролей.",
        };

        public Type? Supersedes => null;

        public static string S1_Context = """
            ## Контекст и постановка задачи

            Фич-страниц несколько, и число будет расти. Нужно зафиксировать:

            - как декларируются маршруты (один файл против корневой + per-feature);
            - где живёт **реестр имён сегментов**, чтобы избежать литералов URL в коде;
            - какова URL-структура.

            Защита маршрутов гардами (аутентификация, права) зависит от ролей и доступа, которые пока не
            заданы (см. `IcdVision`), — выносится в отдельный ADR.
            """;

        public static string S2_DecisionDrivers = """
            ## Драйверы решения

            1. **Lazy loading** — фичи подгружаются по требованию; стартовый bundle небольшой.
            2. **Единый источник имён сегментов** — нет литералов вроде `'/input'` в коде; переименование
               URL — одна точка правки.
            3. **Плоская структура** (ADR-UI-003) — URL не повторяет навигационную группировку меню.
            4. **Декларативность** — маршрут фичи объявляет сама фича.
            """;

        public static string S3_ConsideredOptions = """
            ## Рассмотренные варианты

            - **A. Один файл `app.routes.ts`** со всеми маршрутами — просто, но тащит код всех фич в
              initial bundle и плохо масштабируется.
            - **B. Корневой `app.routes.ts` + per-feature `<feature>.routes.ts`, lazy-loaded** — каждая
              фича объявляет свои маршруты.
            - **C. Группировка маршрутов по разделам меню** (`/admin/users`) — лишняя вложенность в URL и
              файлах.
            """;

        public static string S4_DecisionOutcome = """
            ## Решение

            Выбран **Вариант B**: корневой `app.routes.ts` + per-feature маршруты, lazy-loaded.

            ### Оболочка и стартовая страница

            Оболочка приложения (шапка + рабочая область) — это корневой компонент `App` (ADR-UI-005),
            **не** маршрут-обёртка: все страницы рендерятся в его `router-outlet`. Стартовый `''` ведёт на
            **главную** (`HomePage`) напрямую. Когда появится режим «без оболочки» (экраны входа) и защита
            доступа — layout вынесется в маршрут-обёртку отдельным ADR.

            ### Корневой `src/app/app.routes.ts`

            ```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 },
            ];
            ```

            ### Per-feature `<feature>.routes.ts`

            Объявляет внутреннюю структуру фичи (список / создание / карточка). Внутренние сегменты — в
            реестре фичи, если их больше двух-трёх.

            ### Реестр сегментов — `src/app/services/router/`

            Единый источник имён; классы без префикса (ADR-UI-002):

            ```ts
            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' }`), когда их несколько.

            ### URL-структура

            - **Плоская**: `/input`, `/queue`, `/cards`, `/documents`, `/admin`.
            - **Без префикса групп** навигации — группировка живёт только в меню (ADR-UI-005).
            - **Корневой `''`** → главная.

            ### Правила использования

            1. **Никаких литералов URL в коде.** Сборка пути — через `BaseRoutes` / `RouteParam`
               (`routerLink`, `router.navigate`).
            2. **Внутренние сегменты фичи** — в реестре фичи, не дублируются между маршрутами и
               компонентами.
            3. **Lazy load обязателен** для фич (несколько страниц или свои `services`/`models`);
               одностраничные — через `loadComponent`.
            """;

        public static string S5_PositiveConsequences = """
            ## Положительные следствия

            - Initial bundle — только оболочка и общие компоненты; фичи грузятся по требованию.
            - Реестр сегментов — единая точка правки при переименовании путей.
            - Маршрут фичи объявляет сама фича — структура не расползается.
            """;

        public static string S6_NegativeConsequences = """
            ## Отрицательные следствия и компромиссы

            - URL не отражает навигационную группировку — при необходимости (deep-links) пересмотрим.
            - Реестр `BaseRoutes` нужно держать в синхроне с реальными маршрутами — ревью / линт.
            """;

        public static string S7_Validation = """
            ## Проверка

            - Новая фича добавляется тремя действиями: сегмент в `BaseRoutes` → `modules/<feature>/
              <feature>.routes.ts` → подключение через `loadChildren` в `app.routes.ts`.
            - В коде нет литералов вроде `'/input'` — `grep` подтверждает.
            """;

        public static string S8_OpenQuestions = """
            ## Открытые вопросы / отложено

            - **Гарды доступа** — аутентификация и права на маршрутах (защита оболочки, требуемые права
              на фиче, стартовый редирект по правам) — отдельным ADR при определении ролей/доступа.
            - **Внутренние сегменты фич** — заводятся по мере появления многостраничных фич.
            - **Группировка URL по разделам** — не делаем; при необходимости отдельный ADR.
            """;

        public static string S99_Related = $"""
            ## Связанные артефакты

            - {nameof(Ban.Sdaid.Icd.Arch.Tech.Angular.ADR_UI_003_ProjectStructure)} — Плоская структура модулей.
            - {nameof(Ban.Sdaid.Icd.Arch.Tech.Angular.ADR_UI_005_Layout)} — Оболочка приложения и режимы.
            - {nameof(Ban.Sdaid.Icd.Vision.IcdVision)} — Роли и доступ (открытый вопрос).
            """;
    }
}