ADR-UI-003. Структура проекта src-ui
Каркас src-ui, созданный ng new, минимален. По мере роста (страницы, сервисы, компоненты,
модели, гарды) нужен предсказуемый физический layout: чтобы человек и ИИ одинаково находили место
для нового кода, а генераторы опирались на стабильные пути. Учитываем переносимость набора ui/ и
отсутствие внешнего UI-kit.
1. Контекст и постановка задачи
Нужен предсказуемый физический layout src-ui, чтобы:
- человек и ИИ-ассистент одинаково находили место для нового кода;
- генераторы/skills опирались на стабильные пути;
- структура не разъезжалась при добавлении фич.
Две особенности ICD, отличающие раскладку от типовой: собственный переносимый набор
компонентов ui/ (должен быть самодостаточным, включая дизайн-токены) и отсутствие
внешнего UI-kit (нет antd-overrides и подобного).
2. Драйверы решения
- Единственная ответственность папок — каждая папка верхнего уровня имеет понятное назначение, новый код легко классифицировать.
- Плоские модули — никакой группировки
modules/<group>/<feature>/; навигационная группировка живёт в меню, не в файловой системе. - Lazy loading — каждая фича
modules/<feature>/подключается ленивыми маршрутами. - Стабильные пути — для автоматизации и предсказуемого ревью.
- Самодостаточность
ui/— набор с токенами переносится в другой проект целиком, без внешних стилевых зависимостей.
3. Рассмотренные варианты
- A. Тематические папки верхнего уровня + плоские фичи —
app/{consts, directives, guards, models, modules, pipes, services, ui, utils}, фичи рядом вmodules/. - B. Двухуровневые модули —
modules/<group>/<feature>/; навигационная группировка дублируется в файловой системе. - C. Feature-first — каждая фича со своими
services/components/modelsобособленно, без общих тематических папок.
4. Решение
Выбран Вариант A. Папки создаются по мере надобности (не заводим пустые заранее); назначение каждой зафиксировано ниже. Именование файлов — по стилю Angular 22 без суффиксов (см. ADR-UI-002).
4.1. Дерево верхнего уровня (целевое)
src-ui/
└── src/
├── app/
│ ├── consts/ # Глобальные константы
│ ├── directives/ # Атрибутивные/структурные директивы
│ ├── guards/ # CanActivate-гарды
│ ├── models/ # Общие модели (Option, Pagination, Sorting, …)
│ ├── modules/ # Фичи (плоско, см. ниже)
│ ├── pipes/ # Глобальные pipe-ы
│ ├── services/ # Singleton-сервисы (root)
│ │ ├── api-clients/ # Клиенты к backend (генерируемые; см. ADR про API)
│ │ └── router/ # Реестры маршрутов
│ ├── ui/ # Собственный переносимый набор компонентов (см. ниже)
│ ├── utils/ # Чистые функции
│ ├── app.ts
│ ├── app.config.ts
│ ├── app.routes.ts
│ └── app.const.ts
├── styles/
│ └── theme/ # Переопределения токенов набора под проект (тема)
├── styles.scss # Точка входа стилей (подключает токены ui/ и styles/theme/)
├── index.html
└── main.ts
4.2. Набор ui/ — самодостаточный и переносимый
app/ui/
├── tokens/ # Дизайн-токены (CSS custom properties): палитра, отступы, типографика.
│ # Едут ВМЕСТЕ с набором — часть его контракта.
├── <component>/ # Папка на компонент (button/, table/, …): <name>.ts|html|scss + index.ts
└── index.ts # Общий barrel набора
Набор зависит только «вниз» (Angular, @angular/cdk, свои токены) и не импортирует доменный
код (см. ADR-UI-002, принципы переносимости).
4.3. Структура папки фичи (modules/<feature>/)
modules/<feature>/
├── components/ # Под-компоненты страниц
├── models/ # Доменные UI-модели фичи
├── pages/
│ └── <page>/
│ ├── <page>.ts
│ ├── <page>.html
│ └── <page>.scss
├── services/ # Per-page сервисы (регистрируются в providers страницы)
└── <feature>.routes.ts # Ленивые маршруты фичи
4.4. Принципы организации
- Модули — плоско. Никакого
modules/<group>/<feature>/; группировка — только в меню. ui/— переиспользуемые виджеты, не привязанные к фиче; самодостаточны (с токенами).services/(app) — только глобальные singleton +api-clients/иrouter/. Per-page сервисы — вmodules/<feature>/services/.models/(app) — только общие; доменные — в фиче.- Без
store/— глобального store нет (ADR-UI-001).
4.5. Стили и кастомизация набора
- Стили — SCSS; дизайн-токены — CSS custom properties в
app/ui/tokens/(переносятся с набором). - Кастомизация под проект — переопределением значений токенов в
src/styles/theme/: меняет тему глобально, компоненты не трогаются (чистая замена подхода «antd-overrides»). - Точечная кастомизация — через component-scoped переменные, которые компонент осознанно
экспонирует (
--icd-<component>-<prop>). Инкапсулированные стили компонентов снаружи не «ломаем» (ViewEncapsulation) — только предусмотренные переменные.
4.6. TS-алиасы (tsconfig.json)
{
"compilerOptions": {
"paths": {
"@app/*": ["src/app/*"],
"@api/*": ["src/app/services/api-clients/*"],
"@ui/*": ["src/app/ui/*"]
}
}
}
Алиасы обязательны для всего, что вне текущей папки фичи — импорты устойчивы к рефакторингу
путей. Для набора ui/ алиас @ui/* дополнительно облегчает будущий вынос в библиотеку.
4.7. API-клиенты
Место — app/services/api-clients/<context>/. Список контекстов и способ генерации клиента из
backend-контракта уточняются отдельным ADR при появлении API; сейчас фиксируется только место.
5. Положительные следствия
- Плоские
modules/исключают споры «куда положить фичу»; тематические папки дают однозначное место каждой роли кода. - Набор
ui/самодостаточен (компоненты + токены) — переносится и кастомизируется без правки компонентов. - Алиасы делают импорты независимыми от глубины фичи и устойчивыми к рефакторингу.
6. Отрицательные следствия и компромиссы
- При росте числа фич
modules/содержит много одноуровневых каталогов — решается навигацией IDE, не структурой. - Доменные модели рассыпаны по
modules/*/models/— цена изоляции фич. - Кастомизация только через токены и предусмотренные переменные требует, чтобы компоненты заранее экспонировали нужные «хуки» — дисциплина при дизайне API компонента.
7. Проверка
- Новая фича добавляется как
modules/<feature>/{components, models, pages, services, <feature>.routes.ts}без правки верхнего уровня. - Любой виджет из
ui/используется из любой фичи без циклических зависимостей и без импорта доменного кода. - Смена палитры/отступов выполняется правкой
src/styles/theme/, без изменения компонентов.
8. Открытые вопросы / отложено
- API-контексты и способ генерации клиентов из backend-контракта — отдельным ADR при появлении эндпоинтов.
validators/— пока не заводим; появится с первыми формами (на Signal Forms).