# 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/` | | Самостоятельная пользовательская возможность с собственным смыслом | `features/` | | Представление/поведение бизнес-сущности | `entities/` | | Переиспользуемый код без 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 не доказывает правильный выбор слоя.