# 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'ов после полной миграции