Files
frontend/.ai/architecture.md
T
ksv741 d9ada27621
Frontend security / security (pull_request) Successful in 8s
Frontend verify / verify (pull_request) Successful in 3m55s
Conventional commits / commits (pull_request) Successful in 20s
Frontend security / security (push) Successful in 8s
Frontend verify / verify (push) Successful in 3m49s
docs: make frontend instructions self-contained
2026-09-15 20:45:37 +03:00

82 lines
5.7 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 частично проверяет
`npm run deps`; Knip проверяет использование exports, а не смысл placement.
## UI foundation
`shared/theme/tokens.json` — переносимый source design tokens; `theme.ts`
адаптирует доверенный JSON к Mantine без runtime schema. App и Storybook читают
public API `shared/theme`. Storybook показывает synthetic presentation states,
не использует application API. RTL/MSW проверяют поведение приложения отдельно.
Existing route-aware AppShell расположен в shared/ui; это legacy placement,
а не образец нового shared primitive. При изменении foundation прочитай
[UI](conventions/ui.md#визуальная-основа) и [Storybook](conventions/storybook.md).
## Примеры
- Общая busy-область: существующий [LoadingSkeleton](../src/shared/ui/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.
Existing dependency config не запрещает все same-layer imports: их и смысл
owner проверяет review. Не объявляй Steiger установленным; graph проверяет
текущий dependency-cruiser. Review отдельно проверяет смысл owner: разрешённый dependency graph не доказывает
правильный выбор слоя.