Files
frontend/.ai/architecture.md
T
ksv741 f876411a37
Frontend security / security (push) Successful in 9s
Frontend verify / verify (push) Successful in 5m27s
Frontend security / security (pull_request) Successful in 10s
Frontend verify / verify (pull_request) Successful in 5m24s
Conventional commits / commits (pull_request) Failing after 26s
refactor: establish local frontend workspace boundaries
OpenSpec change: evolve-frontend-stack
2026-09-22 22:11:02 +03:00

91 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Frontend architecture
## Устройство и data flow
React + TypeScript + Vite SPA. Router задаёт navigation; providers в `app`
подключают auth, TanStack Query, i18n и Mantine. Vite использует React Compiler;
Vitest — отдельную JSDOM конфигурацию. Точные versions/scripts — в
[package.json](../package.json), реализация — source of truth.
```text
app → pages → features → entities → shared
page container → page VM → entity/feature query → shared/api → backend /api/v1
↓
props → state/section views
```
Server state и lifecycle запроса принадлежат TanStack Query; VM собирает данные
для view. Transport — `openapi-fetch`/`openapi-react-query`, типизированные из
предоставленного snapshot через `openapi-typescript`. Backend владеет wire
contract; frontend не читает его checkout для генерации и не вызывает T-Bank.
[Data convention](conventions/data.md) задаёт auth/cache границы.
## Выбор владельца
Сначала найди владельца поведения и ближайший пример. Расширяй existing slice,
если ответственность остаётся связной. Размер файла или возможный будущий reuse
не создают новую архитектурную границу.
| Ответственность | Размещение |
| --- | --- |
| Bootstrap, глобальные providers, routes | `app` |
| Композиция маршрута и поведение, нужное только этому экрану | `pages/<page>` |
| Самостоятельная пользовательская возможность с собственным смыслом | `features/<capability>` |
| Представление/поведение бизнес-сущности | `entities/<entity>` |
| Переиспользуемый код без WealthLedger domain meaning | `shared` |
Локальный FSD profile: `app → pages → features → entities → shared`, без `widgets`.
Крупный уникальный блок остаётся в своей page. Form, mutation или click handler
сами по себе не требуют feature. Два callers не делают domain helper generic shared.
Slice именуется по ответственности, а не `helpers`, `hooks` или `types`.
**Безопасный путь:** при сомнении оставь код у ближайшего допустимого owner.
Извлекай его, когда появилась самостоятельная ответственность или реальный reuse.
Не вводи новый layer и не обходи public API, чтобы устранить неудобный import.
## Imports и public API
Cross-slice consumer использует `@/` и `index.ts` владельца, например
`@/entities/portfolio`. Alias Vite и TypeScript согласован. Другой slice не
импортирует внутренний файл напрямую. Slices `pages`, `features`, `entities` не зависят от соседнего slice своего слоя;
композиция принадлежит вышележащему owner. Existing меж-slice исключения
не являются разрешением добавлять новые.
Направление зависимостей, public imports и запрет соседних slices в
`pages`, `features`, `entities` проверяет `npm run deps`. Внутренние imports
одного slice и связи shared segments этот запрет не ограничивает.
Knip проверяет использование exports, а не смысл placement.
## UI foundation
Private workspace `packages/tokens` экспортирует переносимый source design tokens
как `@wealthledger/tokens/tokens.json`. Private workspace `packages/ui`
адаптирует доверенный JSON к Mantine без runtime schema. App и Storybook читают
`@wealthledger/ui/theme` и явно подключают `@wealthledger/ui/styles.css` после
Mantine CSS. Потребители используют public exports пакетов напрямую. Storybook показывает synthetic presentation states,
не использует application API. RTL/MSW проверяют поведение приложения отдельно.
Route-aware AppShell и его navigation styles принадлежат `app/ui`;
`ProtectedLayout` использует public API этого владельца. При изменении foundation прочитай
[UI](conventions/ui.md#визуальная-основа) и [Storybook](conventions/storybook.md).
Статичные Add/Edit/Delete и публичный `IconProps` принадлежат private
`packages/icons`; package не зависит от UI, domain или motion.
`BankOperationIcon` и mappings остаются у entity. Card/Cards используют package
Add/Edit/Delete; restore/category SVG остаются локальными.
## Примеры
- Общая busy-область: существующий [LoadingSkeleton](../packages/ui/src/LoadingSkeleton.tsx), а не domain widget в shared.
- Account-income state views: [страница](../src/pages/account-income/ui/AccountIncomePage.tsx) и её flat `components/*.view.tsx`; новый page-local filter остаётся здесь.
- Portfolio query: [entity public API](../src/entities/portfolio/index.ts); новая UI-сводка с бизнес-смыслом не становится shared только из-за нескольких экранов.
## Проверка
После placement/import change: `npm run deps`, `npm run typecheck`, `npm run knip`;
после собственного behavior — применимые tests. Финальный gate указан в AGENTS.
Graph `src` и `packages` проверяет текущий dependency-cruiser; зависимости
из packages в код приложения `src` запрещены. Generated `packages/*/dist`
не анализируется как исходник; все package sources остаются в graph. Review отдельно проверяет смысл
owner: разрешённый dependency graph не доказывает правильный выбор слоя.