docs: define frontend fsd app+auth spec, plan, tasks
This commit is contained in:
parent
ad8ff6a875
commit
af36d2e7cf
178
docs/features/frontend-fsd-app-auth/plan.md
Normal file
178
docs/features/frontend-fsd-app-auth/plan.md
Normal 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'ов после полной миграции
|
||||||
92
docs/features/frontend-fsd-app-auth/spec.md
Normal file
92
docs/features/frontend-fsd-app-auth/spec.md
Normal 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 шимами
|
||||||
|
- Экраны входа, регистрации и профиля работают как раньше
|
||||||
|
- Страницы брокера (защищённые) работают как раньше
|
||||||
57
docs/features/frontend-fsd-app-auth/tasks.md
Normal file
57
docs/features/frontend-fsd-app-auth/tasks.md
Normal 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`
|
||||||
Loading…
x
Reference in New Issue
Block a user