docs: define frontend fsd app+auth spec, plan, tasks

This commit is contained in:
Sergey Krylov 2026-06-20 14:28:10 +03:00
parent ad8ff6a875
commit af36d2e7cf
3 changed files with 327 additions and 0 deletions

View File

@ -0,0 +1,178 @@
# Frontend FSD App + Auth — План реализации
## Стратегия миграции
Поэтапный рефакторинг без big-bang. Каждый legacy-файл сначала превращается в re-export shim
после переноса кода. Все изменения — только перегруппировка, поведение не меняется.
## Целевая структура
```
src/
├── app/
│ ├── App.tsx # BrowserRouter + AppRoutes
│ ├── index.ts # barrel (export { default as App } from './App')
│ ├── providers/
│ │ ├── AppProviders.tsx # QueryClientProvider + SessionProvider
│ │ ├── SessionProvider.tsx # Auth orchestration (из context/AuthContext)
│ │ └── index.ts # barrel
│ ├── routing/
│ │ ├── AppRoutes.tsx # Route definitions (из routes.tsx)
│ │ ├── ProtectedRoute.tsx # Auth guard (из components/ProtectedRoute)
│ │ └── index.ts # barrel
│ └── layouts/
│ ├── AppLayout.tsx # Header + nav + Outlet (из components/Layout)
│ └── index.ts # barrel
├── entities/
│ └── session/
│ ├── index.ts # barrel
│ ├── api/
│ │ ├── sessionApi.ts # login, register, refresh, logout, getMe, updateProfile
│ │ └── sessionApi.test.ts # перенос из api/auth.test.ts
│ └── model/
│ ├── sessionContext.ts # SessionContext + SessionContextValue
│ ├── useSession.ts # хук (из hooks/useAuth.ts)
│ └── useSession.test.tsx # перенос из hooks/useAuth.test.tsx
├── pages/
│ ├── LoginPage.tsx # обновлён импорт на @/entities/session
│ ├── RegisterPage.tsx # обновлён импорт
│ └── ProfilePage.tsx # обновлён импорт
├── api/
│ ├── auth.ts → SHIM для entities/session/api/sessionApi
│ ├── client.ts → SHIM (уже есть)
│ ├── responses.ts → SHIM (уже есть)
│ └── ... (остальные файлы без изменений)
├── hooks/
│ ├── useAuth.ts → SHIM для entities/session/model/useSession
│ └── ... (остальные shim'ы уже есть)
├── context/
│ └── AuthContext.tsx → SHIM (реэкспорт SessionProvider + sessionContext)
├── components/
│ ├── Layout.tsx → SHIM для app/layouts/AppLayout
│ ├── ProtectedRoute.tsx → SHIM для app/routing/ProtectedRoute
│ └── ... (остальные без изменений)
└── main.tsx → обновлён: использует AppProviders
```
## Потоки данных
### Auth flow (без изменений в runtime)
```
User action → Page (LoginPage)
→ useSession().login(email, password)
→ sessionApi.login(email, password) [entities/session/api]
→ request() [shared/api/client]
→ SessionProvider.updateSession(result) [app/providers]
→ setUser + setAccessToken
→ SessionContext.Provider value update
```
### Session restore на старте
```
main.tsx
→ AppProviders
→ SessionProvider (mount)
→ sessionApi.refresh()
→ if ok: updateSession(result)
→ if fail: keep anonymous
→ App → AppRoutes → ProtectedRoute → page
```
## Этапы реализации
### Phase 1: entities/session/
1. Создать `entities/session/api/sessionApi.ts` — скопировать 6 функций из `api/auth.ts`,
заменить импорт `./client` на `@/shared/api/client`, импорт `./responses` на `@/shared/api/responses`
2. Создать `entities/session/api/sessionApi.test.ts` — скопировать из `api/auth.test.ts`,
заменить импорты
3. Создать `entities/session/model/sessionContext.ts` — SessionContext + SessionContextValue
(без Provider-логики, только тип и createContext)
4. Создать `entities/session/model/useSession.ts` — хук useContext с guard
5. Создать `entities/session/model/useSession.test.tsx` — скопировать из `hooks/useAuth.test.tsx`,
заменить импорты (тест пока оборачивает AuthProvider — будет работать через шим)
6. Создать `entities/session/index.ts` — barrel
7. Превратить `api/auth.ts` в shim
8. Превратить `hooks/useAuth.ts` в shim
### Phase 2: app/providers/
1. Создать `app/providers/SessionProvider.tsx` — перенести логику из `context/AuthContext.tsx`:
- Импортирует sessionApi из entities/session
- Использует SessionContext из entities/session
- Весь lifecycle (refresh on mount, updateSession, clearSession) остаётся здесь
2. Создать `app/providers/AppProviders.tsx` — композиция QueryClientProvider + SessionProvider
(вынести QueryClient config из main.tsx)
3. Создать `app/providers/SessionProvider.test.tsx` — скопировать из `context/AuthContext.test.tsx`,
заменить импорты
4. Создать `app/providers/index.ts` — barrel
5. Превратить `context/AuthContext.tsx` в shim (реэкспорт SessionProvider и sessionContext)
### Phase 3: app/routing/
1. Создать `app/routing/ProtectedRoute.tsx` — скопировать из `components/ProtectedRoute.tsx`,
заменить импорт useAuth на useSession из entities/session
2. Создать `app/routing/AppRoutes.tsx` — скопировать из `routes.tsx`:
- Заменить импорт Layout на AppLayout из app/layouts
- Заменить импорт ProtectedRoute на app/routing/ProtectedRoute
- Broker page импорты остаются без изменений
3. Создать `app/routing/index.ts` — barrel
4. Превратить `routes.tsx` в shim
5. Превратить `components/ProtectedRoute.tsx` в shim
### Phase 4: app/layouts/
1. Создать `app/layouts/AppLayout.tsx` — скопировать из `components/Layout.tsx`:
- Заменить импорт useAuth на useSession из entities/session
- Заменить импорт SearchBar на @/components/SearchBar (SearchBar пока не мигрируется)
2. Создать `app/layouts/index.ts` — barrel
3. Превратить `components/Layout.tsx` в shim
### Phase 5: app/App.tsx
1. Создать `app/App.tsx` — из `App.tsx`, BrowserRouter + AppRoutes из app/routing
2. Создать `app/index.ts` — barrel
3. Превратить `App.tsx` в shim
### Phase 6: main.tsx
1. Обновить `main.tsx` — импортировать AppProviders из app/providers
2. Убрать прямой импорт QueryClientProvider и AuthProvider
### Phase 7: update pages
1. `pages/LoginPage.tsx` — заменить `../hooks/useAuth` на `@/entities/session`
2. `pages/RegisterPage.tsx` — аналогично
3. `pages/ProfilePage.tsx` — аналогично
4. Запустить тесты, lint, build
## Ключевые решения
### Почему `entities/session`, а не `features/auth`
- User session — это бизнес-сущность (текущий пользователь, токен, статус аутентификации)
- Auth actions (login/register/logout) — это операции над сессией
- Provider-оркестрация вынесена в `app/providers/`, так как это инфраструктурный код
### Почему context не в entities, а в app/providers
- React-провайдер — это механизм связывания (DI/Infrastructure), а не бизнес-логика
- `SessionProvider` идёт в `app/`, где размещается вся корневая инфраструктура приложения
- SessionContext (тип + createContext) остаётся в `entities/session/model/`, чтобы entities
могли определять свой контракт
### Coexistence
Все legacy файлы остаются как re-export шимы, чтобы:
- Не ломать импорты в ещё не мигрированных файлах
- Позволить поэтапный переход
- Упростить удаление shim'ов после полной миграции

View File

@ -0,0 +1,92 @@
# Frontend FSD App + Auth
Дата: 2026-06-20
Статус: спецификация
## Концепт
Frontend проекта прошёл три итерации FSD-миграции: broker pilot, shared layer, entities migration.
Теперь существующие FSD-слои (entities, widgets, shared) не имеют соответствующей
инфраструктуры `app/` — корневого слоя приложения, в который выносятся entry point, routing,
провайдеры и layout. Параллельно auth-домен остаётся в legacy-структуре (`api/auth.ts`,
`context/AuthContext.tsx`, `hooks/useAuth.ts`), что не позволяет завершить вертикальный срез
auth/user session.
## Цель
Создать FSD-слой `app/` и мигрировать auth-домен в `entities/session/` и `app/providers/`,
завершив тем сквозной путь от корня приложения до domain entities через все FSD-слои.
## Область изменений
### Входит
- Создание `app/` layer: entry point (`App.tsx`), routing (`AppRoutes`, `ProtectedRoute`),
providers (`SessionProvider`, `AppProviders`), layout (`AppLayout`)
- Создание `entities/session/` для auth-домена: session API, session context, useSession hook
- Перенос тестов вслед за кодом
- Превращение исходных legacy-файлов в re-export shims
- Обновление импортов в auth-страницах
### Не входит
- Миграция `api/screener.ts`, `hooks/useScreener.ts`, `components/screener/*` — следующий этап
- Миграция `api/broker.ts`, `api/broker.test.ts` — dead code, удаление в отдельной задаче
- Миграция `components/SearchBar.tsx`, `hooks/useSearch.ts` — следующий этап
- Миграция `components/portfolios/*`, `pages/portfolios/*` — следующий этап
- Миграция `pages/StockPage.tsx`, `pages/BondPage.tsx`, `pages/HomePage.tsx` — следующий этап
- Изменение backend-контрактов, Swagger, CI/CD
- Введение import guards или ESLint boundaries
## Требования
### 1. App-слой приложения
- `app/App.tsx` — entry point: BrowserRouter + AppRoutes, без провайдеров
- `app/routing/AppRoutes.tsx` — все route definitions (аналог текущего `routes.tsx`)
- `app/routing/ProtectedRoute.tsx` — guard для аутентифицированных маршрутов
- `app/layouts/AppLayout.tsx` — корневой layout (header, навигация, outlet)
- `app/providers/AppProviders.tsx` — композиция корневых провайдеров: QueryClientProvider + SessionProvider
- `app/providers/SessionProvider.tsx` — провайдер сессии (управление жизненным циклом auth)
- `main.tsx` — использует AppProviders вместо ручной композиции
### 2. Session-слой
- `entities/session/api/sessionApi.ts` — чистые функции запросов
(login, register, refresh, logout, getMe, updateProfile) через shared/api/client
- `entities/session/model/sessionContext.ts` — SessionContext + SessionContextValue
- `entities/session/model/useSession.ts` — хук доступа к контексту
- `entities/session/index.ts` — barrel export
### 3. Coexistence через shims
Все старые файлы становятся re-export шимами:
- `api/auth.ts` → shim для entities/session/api/sessionApi
- `hooks/useAuth.ts` → shim для entities/session/model/useSession
- `context/AuthContext.tsx` → shim для app/providers/SessionProvider + entities/session/model/sessionContext
- `routes.tsx` → shim для app/routing/AppRoutes
- `App.tsx` → shim для app/App
- `components/Layout.tsx` → shim для app/layouts/AppLayout
- `components/ProtectedRoute.tsx` → shim для app/routing/ProtectedRoute
### 4. Тесты следуют за кодом
- `api/auth.test.ts``entities/session/api/sessionApi.test.ts`
- `context/AuthContext.test.tsx``app/providers/SessionProvider.test.tsx`
- `hooks/useAuth.test.tsx``entities/session/model/useSession.test.tsx`
### 5. Поведение не меняется
Никакой функциональности не добавляется и не изменяется. Только перегруппировка кода.
## Acceptance Criteria
- `app/App.tsx`, `app/providers/`, `app/routing/`, `app/layouts/` существуют с указанной структурой
- `entities/session/` существует с api/model/index.ts
- Все тесты проходят (`npm test -w apps/frontend`)
- Lint проходит (`npm run lint -w apps/frontend`)
- Build проходит (`npm run build -w apps/frontend`)
- Все legacy файлы стали re-export шимами
- Экраны входа, регистрации и профиля работают как раньше
- Страницы брокера (защищённые) работают как раньше

View File

@ -0,0 +1,57 @@
# Frontend FSD App + Auth — Задачи
## Phase 1: entities/session/
- [ ] 1.1 Создать `entities/session/api/sessionApi.ts` (6 функций из `api/auth.ts`, импорты через `@/shared/`)
- [ ] 1.2 Создать `entities/session/api/sessionApi.test.ts` (из `api/auth.test.ts`, обновить импорты)
- [ ] 1.3 Создать `entities/session/model/sessionContext.ts` (SessionContext + SessionContextValue)
- [ ] 1.4 Создать `entities/session/model/useSession.ts` (useContext с guard)
- [ ] 1.5 Создать `entities/session/model/useSession.test.tsx` (из `hooks/useAuth.test.tsx`, обновить импорты)
- [ ] 1.6 Создать `entities/session/index.ts` (barrel)
- [ ] 1.7 Превратить `api/auth.ts` в shim
- [ ] 1.8 Превратить `hooks/useAuth.ts` в shim
- [ ] 1.9 Проверить: `npm test -w apps/frontend`
## Phase 2: app/providers/
- [ ] 2.1 Создать `app/providers/SessionProvider.tsx` (lifecycle из `context/AuthContext.tsx`)
- [ ] 2.2 Создать `app/providers/AppProviders.tsx` (QueryClientProvider + SessionProvider)
- [ ] 2.3 Создать `app/providers/SessionProvider.test.tsx` (из `context/AuthContext.test.tsx`)
- [ ] 2.4 Создать `app/providers/index.ts` (barrel)
- [ ] 2.5 Превратить `context/AuthContext.tsx` в shim
- [ ] 2.6 Проверить: `npm test -w apps/frontend`
## Phase 3: app/routing/
- [ ] 3.1 Создать `app/routing/ProtectedRoute.tsx` (из `components/ProtectedRoute.tsx`, useSession)
- [ ] 3.2 Создать `app/routing/AppRoutes.tsx` (из `routes.tsx`, импорты из app/layouts, app/routing)
- [ ] 3.3 Создать `app/routing/index.ts` (barrel)
- [ ] 3.4 Превратить `routes.tsx` в shim
- [ ] 3.5 Превратить `components/ProtectedRoute.tsx` в shim
- [ ] 3.6 Проверить: `npm test -w apps/frontend`
## Phase 4: app/layouts/
- [ ] 4.1 Создать `app/layouts/AppLayout.tsx` (из `components/Layout.tsx`, useSession)
- [ ] 4.2 Создать `app/layouts/index.ts` (barrel)
- [ ] 4.3 Превратить `components/Layout.tsx` в shim
- [ ] 4.4 Проверить: `npm test -w apps/frontend`
## Phase 5: app/App.tsx
- [ ] 5.1 Создать `app/App.tsx` (BrowserRouter + AppRoutes)
- [ ] 5.2 Создать `app/index.ts` (barrel)
- [ ] 5.3 Превратить `App.tsx` в shim
- [ ] 5.4 Проверить: `npm test -w apps/frontend`
## Phase 6: main.tsx
- [ ] 6.1 Обновить `main.tsx` (AppProviders вместо ручной композиции)
- [ ] 6.2 Проверить: `npm test -w apps/frontend`
## Phase 7: update pages
- [ ] 7.1 `pages/LoginPage.tsx` — импорт `@/entities/session`
- [ ] 7.2 `pages/RegisterPage.tsx` — импорт `@/entities/session`
- [ ] 7.3 `pages/ProfilePage.tsx` — импорт `@/entities/session`
- [ ] 7.4 Финальная проверка: `npm test -w apps/frontend && npm run lint -w apps/frontend && npm run build -w apps/frontend`