diff --git a/apps/frontend/src/api/client.ts b/apps/frontend/src/api/client.ts index 952ffbe..00460e3 100644 --- a/apps/frontend/src/api/client.ts +++ b/apps/frontend/src/api/client.ts @@ -5,13 +5,8 @@ export { setOnUnauthorized, getHealth, searchSecurities, - getShare, - getShareMarketData, - getShareDividends, - getShareHistory, getBond, getBondMarketData, getBondHistory, - getShareCandles, getBondCandles, } from '../shared/api/client'; diff --git a/apps/frontend/src/entities/stock/api/stockApi.ts b/apps/frontend/src/entities/stock/api/stockApi.ts new file mode 100644 index 0000000..8831851 --- /dev/null +++ b/apps/frontend/src/entities/stock/api/stockApi.ts @@ -0,0 +1,53 @@ +import { request } from '@/shared/api/client'; +import type { + ApiResponseMeta, + ShareResponse, + StockMarketData, + DividendItem, + ShareHistoryItem, + CandleItem, +} from '@/shared/api/responses'; + +export function getShare(secid: string): Promise<{ data: ShareResponse; meta: ApiResponseMeta }> { + return request(`/api/v1/securities/shares/${encodeURIComponent(secid)}`); +} + +export function getShareMarketData( + secid: string, +): Promise<{ data: StockMarketData; meta: ApiResponseMeta }> { + return request( + `/api/v1/securities/shares/${encodeURIComponent(secid)}/marketdata`, + ); +} + +export function getShareDividends( + secid: string, +): Promise<{ data: DividendItem[]; meta: ApiResponseMeta }> { + return request( + `/api/v1/securities/shares/${encodeURIComponent(secid)}/dividends`, + ); +} + +export function getShareHistory( + secid: string, + from: string, + till: string, +): Promise<{ data: ShareHistoryItem[]; meta: ApiResponseMeta }> { + return request( + `/api/v1/securities/shares/${encodeURIComponent(secid)}/history`, + { from, till }, + ); +} + +export function getShareCandles( + secid: string, + interval: '1h' | '24h', + from: string, + till: string, +): Promise<{ data: CandleItem[]; meta: ApiResponseMeta }> { + return request(`/api/v1/securities/shares/${encodeURIComponent(secid)}/candles`, { + interval, + from, + till, + }); +} diff --git a/apps/frontend/src/entities/stock/index.ts b/apps/frontend/src/entities/stock/index.ts new file mode 100644 index 0000000..e03b109 --- /dev/null +++ b/apps/frontend/src/entities/stock/index.ts @@ -0,0 +1,10 @@ +export { useStock } from './model/useStock'; +export { useStockCandles } from './model/useStockCandles'; +export { useStockDividends } from './model/useStockDividends'; +export { + getShare, + getShareMarketData, + getShareDividends, + getShareHistory, + getShareCandles, +} from './api/stockApi'; diff --git a/apps/frontend/src/hooks/useStock.test.tsx b/apps/frontend/src/entities/stock/model/useStock.test.tsx similarity index 100% rename from apps/frontend/src/hooks/useStock.test.tsx rename to apps/frontend/src/entities/stock/model/useStock.test.tsx diff --git a/apps/frontend/src/entities/stock/model/useStock.ts b/apps/frontend/src/entities/stock/model/useStock.ts new file mode 100644 index 0000000..c85b25c --- /dev/null +++ b/apps/frontend/src/entities/stock/model/useStock.ts @@ -0,0 +1,14 @@ +import { useQuery } from '@tanstack/react-query'; +import { getShare } from '../api/stockApi'; +import type { ShareResponse } from '@/shared/api/responses'; + +export function useStock(secid: string) { + return useQuery({ + queryKey: ['stock', secid], + queryFn: async () => { + const res = await getShare(secid); + return res.data; + }, + staleTime: 900_000, + }); +} diff --git a/apps/frontend/src/hooks/useStockCandles.test.tsx b/apps/frontend/src/entities/stock/model/useStockCandles.test.tsx similarity index 97% rename from apps/frontend/src/hooks/useStockCandles.test.tsx rename to apps/frontend/src/entities/stock/model/useStockCandles.test.tsx index 0efcdb4..be437f4 100644 --- a/apps/frontend/src/hooks/useStockCandles.test.tsx +++ b/apps/frontend/src/entities/stock/model/useStockCandles.test.tsx @@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest'; import { renderHook, waitFor } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { http, HttpResponse } from 'msw'; -import { server } from '../test/server'; +import { server } from '../../../test/server'; import { useStockCandles } from './useStockCandles'; import { type ReactNode } from 'react'; diff --git a/apps/frontend/src/entities/stock/model/useStockCandles.ts b/apps/frontend/src/entities/stock/model/useStockCandles.ts new file mode 100644 index 0000000..297ba29 --- /dev/null +++ b/apps/frontend/src/entities/stock/model/useStockCandles.ts @@ -0,0 +1,14 @@ +import { useQuery } from '@tanstack/react-query'; +import { getShareCandles } from '../api/stockApi'; +import type { CandleItem } from '@/shared/api/responses'; + +export function useStockCandles(secid: string, interval: '1h' | '24h', from: string, till: string) { + return useQuery({ + queryKey: ['stockCandles', secid, interval, from, till], + queryFn: async () => { + const res = await getShareCandles(secid, interval, from, till); + return res.data; + }, + staleTime: 3600_000, + }); +} diff --git a/apps/frontend/src/hooks/useStockDividends.test.tsx b/apps/frontend/src/entities/stock/model/useStockDividends.test.tsx similarity index 96% rename from apps/frontend/src/hooks/useStockDividends.test.tsx rename to apps/frontend/src/entities/stock/model/useStockDividends.test.tsx index bae8807..9e899ae 100644 --- a/apps/frontend/src/hooks/useStockDividends.test.tsx +++ b/apps/frontend/src/entities/stock/model/useStockDividends.test.tsx @@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest'; import { renderHook, waitFor } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { http, HttpResponse } from 'msw'; -import { server } from '../test/server'; +import { server } from '../../../test/server'; import { useStockDividends } from './useStockDividends'; import { type ReactNode } from 'react'; diff --git a/apps/frontend/src/entities/stock/model/useStockDividends.ts b/apps/frontend/src/entities/stock/model/useStockDividends.ts new file mode 100644 index 0000000..82c9a5c --- /dev/null +++ b/apps/frontend/src/entities/stock/model/useStockDividends.ts @@ -0,0 +1,14 @@ +import { useQuery } from '@tanstack/react-query'; +import { getShareDividends } from '../api/stockApi'; +import type { DividendItem } from '@/shared/api/responses'; + +export function useStockDividends(secid: string) { + return useQuery({ + queryKey: ['stockDividends', secid], + queryFn: async () => { + const res = await getShareDividends(secid); + return res.data; + }, + staleTime: 86400_000, + }); +} diff --git a/apps/frontend/src/hooks/useStock.ts b/apps/frontend/src/hooks/useStock.ts index a650025..11ff4cb 100644 --- a/apps/frontend/src/hooks/useStock.ts +++ b/apps/frontend/src/hooks/useStock.ts @@ -1,14 +1 @@ -import { useQuery } from '@tanstack/react-query'; -import { getShare } from '@/shared/api/client'; -import type { ShareResponse } from '@/shared/api/responses'; - -export function useStock(secid: string) { - return useQuery({ - queryKey: ['stock', secid], - queryFn: async () => { - const res = await getShare(secid); - return res.data; - }, - staleTime: 900_000, - }); -} +export { useStock } from '../entities/stock/model/useStock'; diff --git a/apps/frontend/src/hooks/useStockCandles.ts b/apps/frontend/src/hooks/useStockCandles.ts index 836f9dc..644306c 100644 --- a/apps/frontend/src/hooks/useStockCandles.ts +++ b/apps/frontend/src/hooks/useStockCandles.ts @@ -1,14 +1 @@ -import { useQuery } from '@tanstack/react-query'; -import { getShareCandles } from '@/shared/api/client'; -import type { CandleItem } from '@/shared/api/responses'; - -export function useStockCandles(secid: string, interval: '1h' | '24h', from: string, till: string) { - return useQuery({ - queryKey: ['stockCandles', secid, interval, from, till], - queryFn: async () => { - const res = await getShareCandles(secid, interval, from, till); - return res.data; - }, - staleTime: 3600_000, - }); -} +export { useStockCandles } from '../entities/stock/model/useStockCandles'; diff --git a/apps/frontend/src/hooks/useStockDividends.ts b/apps/frontend/src/hooks/useStockDividends.ts index 7a80600..563ae92 100644 --- a/apps/frontend/src/hooks/useStockDividends.ts +++ b/apps/frontend/src/hooks/useStockDividends.ts @@ -1,14 +1 @@ -import { useQuery } from '@tanstack/react-query'; -import { getShareDividends } from '@/shared/api/client'; -import type { DividendItem } from '@/shared/api/responses'; - -export function useStockDividends(secid: string) { - return useQuery({ - queryKey: ['stockDividends', secid], - queryFn: async () => { - const res = await getShareDividends(secid); - return res.data; - }, - staleTime: 86400_000, - }); -} +export { useStockDividends } from '../entities/stock/model/useStockDividends'; diff --git a/apps/frontend/src/pages/StockPage.tsx b/apps/frontend/src/pages/StockPage.tsx index cff6179..bfe90ce 100644 --- a/apps/frontend/src/pages/StockPage.tsx +++ b/apps/frontend/src/pages/StockPage.tsx @@ -1,7 +1,5 @@ import { useParams } from 'react-router-dom'; -import { useStock } from '../hooks/useStock'; -import { useStockCandles } from '../hooks/useStockCandles'; -import { useStockDividends } from '../hooks/useStockDividends'; +import { useStock, useStockCandles, useStockDividends } from '../entities/stock'; import { StockDetails } from '../components/StockDetails'; import { PriceChart } from '../components/PriceChart'; diff --git a/apps/frontend/src/shared/api/client.ts b/apps/frontend/src/shared/api/client.ts index c6f0873..b7827a6 100644 --- a/apps/frontend/src/shared/api/client.ts +++ b/apps/frontend/src/shared/api/client.ts @@ -2,10 +2,6 @@ import type { ApiEnvelope, ApiResponseMeta, AuthResponse, - ShareResponse, - StockMarketData, - DividendItem, - ShareHistoryItem, BondResponse, BondMarketData, BondHistoryItem, @@ -151,37 +147,6 @@ export function searchSecurities( }); } -export function getShare(secid: string): Promise<{ data: ShareResponse; meta: ApiResponseMeta }> { - return request(`/api/v1/securities/shares/${encodeURIComponent(secid)}`); -} - -export function getShareMarketData( - secid: string, -): Promise<{ data: StockMarketData; meta: ApiResponseMeta }> { - return request( - `/api/v1/securities/shares/${encodeURIComponent(secid)}/marketdata`, - ); -} - -export function getShareDividends( - secid: string, -): Promise<{ data: DividendItem[]; meta: ApiResponseMeta }> { - return request( - `/api/v1/securities/shares/${encodeURIComponent(secid)}/dividends`, - ); -} - -export function getShareHistory( - secid: string, - from: string, - till: string, -): Promise<{ data: ShareHistoryItem[]; meta: ApiResponseMeta }> { - return request( - `/api/v1/securities/shares/${encodeURIComponent(secid)}/history`, - { from, till }, - ); -} - export function getBond(secid: string): Promise<{ data: BondResponse; meta: ApiResponseMeta }> { return request(`/api/v1/securities/bonds/${encodeURIComponent(secid)}`); } @@ -205,19 +170,6 @@ export function getBondHistory( ); } -export function getShareCandles( - secid: string, - interval: '1h' | '24h', - from: string, - till: string, -): Promise<{ data: CandleItem[]; meta: ApiResponseMeta }> { - return request(`/api/v1/securities/shares/${encodeURIComponent(secid)}/candles`, { - interval, - from, - till, - }); -} - export function getBondCandles( secid: string, interval: '1h' | '24h', diff --git a/apps/frontend/src/shared/api/index.ts b/apps/frontend/src/shared/api/index.ts index 9321a97..048384a 100644 --- a/apps/frontend/src/shared/api/index.ts +++ b/apps/frontend/src/shared/api/index.ts @@ -5,14 +5,9 @@ export { setOnUnauthorized, getHealth, searchSecurities, - getShare, - getShareMarketData, - getShareDividends, - getShareHistory, getBond, getBondMarketData, getBondHistory, - getShareCandles, getBondCandles, } from './client'; export type { diff --git a/docs/features/fsd-entities-migration/plan.md b/docs/features/fsd-entities-migration/plan.md new file mode 100644 index 0000000..856ff06 --- /dev/null +++ b/docs/features/fsd-entities-migration/plan.md @@ -0,0 +1,178 @@ +# FSD Entities Migration — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. + +**Goal:** Перенести stock, bond и portfolio домены в FSD-сущности (`entities/`) без изменения пользовательского поведения. + +**Architecture:** Миграция по паттерну broker-пилота: создаётся FSD-каркас entity → код переносится → старые файлы становятся shim-ами → потребители переключаются на новые импорты → общий клиент очищается. + +**Tech Stack:** React 18, TypeScript, React Router v6, TanStack Query v5, Vitest, Testing Library. + +--- + +## Карта файлов + +### Stock entity + +- Create `apps/frontend/src/entities/stock/index.ts` +- Create `apps/frontend/src/entities/stock/api/stockApi.ts` +- Create `apps/frontend/src/entities/stock/model/useStock.ts` +- Create `apps/frontend/src/entities/stock/model/useStockCandles.ts` +- Create `apps/frontend/src/entities/stock/model/useStockDividends.ts` + +### Bond entity + +- Create `apps/frontend/src/entities/bond/index.ts` +- Create `apps/frontend/src/entities/bond/api/bondApi.ts` +- Create `apps/frontend/src/entities/bond/model/useBond.ts` +- Create `apps/frontend/src/entities/bond/model/useBondCandles.ts` + +### Portfolio entity + +- Create `apps/frontend/src/entities/portfolio/index.ts` +- Create `apps/frontend/src/entities/portfolio/api/portfolioApi.ts` +- Create `apps/frontend/src/entities/portfolio/model/usePortfolio.ts` +- Create `apps/frontend/src/entities/portfolio/model/usePortfolios.ts` +- Create `apps/frontend/src/entities/portfolio/model/usePortfolioAnalytics.ts` +- Create `apps/frontend/src/entities/portfolio/model/usePortfolioMutations.ts` +- Create `apps/frontend/src/entities/portfolio/model/usePositionMutations.ts` + +### Shared cleanup + +- Modify `apps/frontend/src/shared/api/client.ts` — удалить 9 stock/bond API-функций +- Modify `apps/frontend/src/shared/api/index.ts` — удалить re-exports stock/bond функций +- Modify `apps/frontend/src/api/client.ts` — удалить re-exports stock/bond функций (shim) + +### Consumer migration + +- Modify `apps/frontend/src/pages/StockPage.tsx` — переключить импорты на `../../entities/stock` +- Modify `apps/frontend/src/pages/BondPage.tsx` — переключить импорты на `../../entities/bond` +- Modify `apps/frontend/src/pages/portfolios/PortfolioDetailPage.tsx` — на `../../entities/portfolio` +- Modify `apps/frontend/src/pages/portfolios/PortfoliosListPage.tsx` — на `../../entities/portfolio` + +### Shim files (обратно-совместимые re-export) + +- Modify `apps/frontend/src/hooks/useStock.ts` → shim +- Modify `apps/frontend/src/hooks/useStockCandles.ts` → shim +- Modify `apps/frontend/src/hooks/useStockDividends.ts` → shim +- Modify `apps/frontend/src/hooks/useBond.ts` → shim +- Modify `apps/frontend/src/hooks/useBondCandles.ts` → shim +- Modify `apps/frontend/src/hooks/usePortfolio.ts` → shim +- Modify `apps/frontend/src/hooks/usePortfolios.ts` → shim +- Modify `apps/frontend/src/hooks/usePortfolioAnalytics.ts` → shim +- Modify `apps/frontend/src/hooks/usePortfolioMutations.ts` → shim +- Modify `apps/frontend/src/hooks/usePositionMutations.ts` → shim +- Modify `apps/frontend/src/api/portfolio.ts` → shim + +### Test relocation + +- Move `apps/frontend/src/hooks/useStock.test.tsx` → `apps/frontend/src/entities/stock/model/useStock.test.tsx` +- Move `apps/frontend/src/hooks/useStockCandles.test.tsx` → `apps/frontend/src/entities/stock/model/useStockCandles.test.tsx` +- Move `apps/frontend/src/hooks/useStockDividends.test.tsx` → `apps/frontend/src/entities/stock/model/useStockDividends.test.tsx` +- Move `apps/frontend/src/hooks/useBond.test.tsx` → `apps/frontend/src/entities/bond/model/useBond.test.tsx` +- Move `apps/frontend/src/hooks/useBondCandles.test.tsx` → `apps/frontend/src/entities/bond/model/useBondCandles.test.tsx` + +--- + +## Task 1: entities/stock/ + +### Файлы + +- Create: `src/entities/stock/index.ts` +- Create: `src/entities/stock/api/stockApi.ts` +- Create: `src/entities/stock/model/useStock.ts` +- Create: `src/entities/stock/model/useStockCandles.ts` +- Create: `src/entities/stock/model/useStockDividends.ts` +- Move: `src/hooks/useStock.test.tsx` → `src/entities/stock/model/useStock.test.tsx` +- Move: `src/hooks/useStockCandles.test.tsx` → `src/entities/stock/model/useStockCandles.test.tsx` +- Move: `src/hooks/useStockDividends.test.tsx` → `src/entities/stock/model/useStockDividends.test.tsx` + +### Связанные изменения + +- Modify: `src/shared/api/client.ts` — удалить `getShare`, `getShareMarketData`, `getShareDividends`, `getShareHistory`, `getShareCandles` +- Modify: `src/shared/api/index.ts` — удалить re-exports stock функций +- Modify: `src/api/client.ts` — удалить re-exports stock функций +- Modify: `src/hooks/useStock.ts` — заменить на shim +- Modify: `src/hooks/useStockCandles.ts` — заменить на shim +- Modify: `src/hooks/useStockDividends.ts` — заменить на shim +- Modify: `src/pages/StockPage.tsx` — переключить импорты + +- [ ] **Step 1: Создать api/stockApi.ts с функциями запросов stock** +- [ ] **Step 2: Создать model хуки для stock (useStock, useStockCandles, useStockDividends)** +- [ ] **Step 3: Создать entities/stock/index.ts barrel** +- [ ] **Step 4: Переместить тесты stock в model/ и обновить импорты** +- [ ] **Step 5: Удалить stock функции из shared/api/client.ts и обновить barrely (shared/api/index.ts, api/client.ts)** +- [ ] **Step 6: Превратить hooks/useStock* в shim-файлы** +- [ ] **Step 7: Переключить StockPage.tsx на импорты из ../../entities/stock** +- [ ] **Step 8: Запустить тесты: npm test -w apps/frontend -- useStock** +- [ ] **Step 9: Закоммитить** + +## Task 2: entities/bond/ + +### Файлы + +- Create: `src/entities/bond/index.ts` +- Create: `src/entities/bond/api/bondApi.ts` +- Create: `src/entities/bond/model/useBond.ts` +- Create: `src/entities/bond/model/useBondCandles.ts` +- Move: `src/hooks/useBond.test.tsx` → `src/entities/bond/model/useBond.test.tsx` +- Move: `src/hooks/useBondCandles.test.tsx` → `src/entities/bond/model/useBondCandles.test.tsx` + +### Связанные изменения + +- Modify: `src/shared/api/client.ts` — удалить `getBond`, `getBondMarketData`, `getBondHistory`, `getBondCandles` +- Modify: `src/shared/api/index.ts` — удалить re-exports bond функций +- Modify: `src/api/client.ts` — удалить re-exports bond функций +- Modify: `src/hooks/useBond.ts` — заменить на shim +- Modify: `src/hooks/useBondCandles.ts` — заменить на shim +- Modify: `src/pages/BondPage.tsx` — переключить импорты + +- [ ] **Step 1: Создать api/bondApi.ts с функциями запросов bond** +- [ ] **Step 2: Создать model хуки для bond (useBond, useBondCandles)** +- [ ] **Step 3: Создать entities/bond/index.ts barrel** +- [ ] **Step 4: Переместить тесты bond в model/ и обновить импорты** +- [ ] **Step 5: Удалить bond функции из shared/api/client.ts и обновить barrely** +- [ ] **Step 6: Превратить hooks/useBond* в shim-файлы** +- [ ] **Step 7: Переключить BondPage.tsx на импорты из ../../entities/bond** +- [ ] **Step 8: Запустить тесты: npm test -w apps/frontend -- useBond** +- [ ] **Step 9: Закоммитить** + +## Task 3: entities/portfolio/ + +### Файлы + +- Create: `src/entities/portfolio/index.ts` +- Create: `src/entities/portfolio/api/portfolioApi.ts` +- Create: `src/entities/portfolio/model/usePortfolio.ts` +- Create: `src/entities/portfolio/model/usePortfolios.ts` +- Create: `src/entities/portfolio/model/usePortfolioAnalytics.ts` +- Create: `src/entities/portfolio/model/usePortfolioMutations.ts` +- Create: `src/entities/portfolio/model/usePositionMutations.ts` + +### Связанные изменения + +- Modify: `src/api/portfolio.ts` — заменить на shim (re-export из entities/portfolio/api/portfolioApi) +- Modify: `src/hooks/usePortfolio.ts` — заменить на shim +- Modify: `src/hooks/usePortfolios.ts` — заменить на shim +- Modify: `src/hooks/usePortfolioAnalytics.ts` — заменить на shim +- Modify: `src/hooks/usePortfolioMutations.ts` — заменить на shim +- Modify: `src/hooks/usePositionMutations.ts` — заменить на shim +- Modify: `src/pages/portfolios/PortfolioDetailPage.tsx` — переключить импорты +- Modify: `src/pages/portfolios/PortfoliosListPage.tsx` — переключить импорты + +- [ ] **Step 1: Создать api/portfolioApi.ts — перенести все функции из api/portfolio.ts** +- [ ] **Step 2: Создать model хуки для portfolio (5 файлов)** +- [ ] **Step 3: Создать entities/portfolio/index.ts barrel** +- [ ] **Step 4: Превратить api/portfolio.ts в shim** +- [ ] **Step 5: Превратить hooks/usePortfolio* и usePositionMutations в shim-файлы** +- [ ] **Step 6: Переключить PortfolioDetailPage.tsx и PortfoliosListPage.tsx на импорты из ../../entities/portfolio** +- [ ] **Step 7: Запустить build: npm run build -w apps/frontend** +- [ ] **Step 8: Закоммитить** + +## Task 4: Финальная проверка + +- [ ] **Step 1: Полный прогон тестов: npm test -w apps/frontend** +- [ ] **Step 2: Линт: npm run lint -w apps/frontend** +- [ ] **Step 3: Сборка: npm run build -w apps/frontend** +- [ ] **Step 4: Проверить git diff --stat на предмет только целевых изменений** +- [ ] **Step 5: Обновить docs/features/fsd-entities-migration/tasks.md** diff --git a/docs/features/fsd-entities-migration/spec.md b/docs/features/fsd-entities-migration/spec.md new file mode 100644 index 0000000..84ee687 --- /dev/null +++ b/docs/features/fsd-entities-migration/spec.md @@ -0,0 +1,81 @@ +# FSD Entities Migration + +Дата: 2026-06-20 +Статус: в работе + +## Контекст + +Продолжается поэтапный переход frontend на Feature-Sliced Design (FSD). После успешного пилота +broker-домена (`frontend-fsd-broker-pilot`) и создания shared-слоя (`frontend-fsd-shared-layer`) +следующий шаг — миграция core domain entities: stock, bond, portfolio. + +В текущей структуре их код размазан по техническим каталогам: +- API-функции акций и облигаций находятся в `shared/api/client.ts` (вместе с HTTP-инфраструктурой) +- API-функции портфелей находятся в `api/portfolio.ts` (в legacy-папке) +- TanStack Query хуки находятся в `hooks/` (5 файлов для portfolio + 3 для stock + 2 для bond) +- Типы уже централизованы в `shared/api/responses.ts` + +Это усложняет навигацию, поддержку и дальнейшее масштабирование FSD-архитектуры. + +## Цель + +Перенести stock, bond и portfolio домены в FSD-сущности (`entities/`) по тому же паттерну, что и +broker-account/broker-position/broker-operation: + +- `entities/stock/` — API-функции и query-хуки для акций +- `entities/bond/` — API-функции и query-хуки для облигаций +- `entities/portfolio/` — API-функции и query-хуки для портфелей + +## Требования + +### 1. Каждая сущность получает явную FSD-структуру + +- `api/` — чистые функции запросов (без TanStack Query, только `request` вызовы) +- `model/` — TanStack Query хуки и бизнес-логика +- `index.ts` — barrel export (только re-exports, без логики) + +### 2. API-функции выносятся из shared/api/client.ts + +`shared/api/client.ts` должен содержать только инфраструктуру: +- `request` — базовый HTTP-клиент +- `setAccessToken`, `getAccessToken`, `setOnUnauthorized` — управление токенами +- `searchSecurities` — поиск (кросс-доменный) +- `getHealth` — health check + +Все stock/bond API-функции переезжают в соответствующие entity `api/`. + +### 3. Query-хуки переносятся из global hooks/ + +Все TanStack Query хуки для stock, bond и portfolio переезжают из `hooks/` в +`entities/{entity}/model/`. + +### 4. Совместимость на время перехода + +Старые файлы становятся re-export шимами, чтобы избежать big-bang переписывания. +Потребители (страницы) обновляются на новые FSD-импорты в рамках этой же фичи. + +### 5. Тесты следуют за кодом + +Unit-тесты и hook-тесты перемещаются рядом с новым расположением кода. + +### 6. Поведение не меняется + +Никакой функциональности не добавляется и не изменяется. Только перегруппировка кода. + +## Ограничения + +- Не затрагиваются `features/` и `app/` слои — они будут в следующих итерациях +- Не затрагиваются `useSearch`, `useScreener`, `api/auth.ts`, `api/screener.ts` +- Не меняются `shared/api/responses.ts` и generated types +- Не меняются backend-контракты + +## Acceptance Criteria + +- `entities/stock/` существует с api/model/index.ts, все тесты проходят +- `entities/bond/` существует с api/model/index.ts, все тесты проходят +- `entities/portfolio/` существует с api/model/index.ts +- `shared/api/client.ts` больше не содержит stock/bond API-функций +- Все страницы (StockPage, BondPage, PortfolioDetailPage, PortfoliosListPage) импортируют + из новых entity entrypoints +- Старые файлы стали re-export шимами +- Frontend lint, tests и build проходят diff --git a/docs/features/fsd-entities-migration/tasks.md b/docs/features/fsd-entities-migration/tasks.md new file mode 100644 index 0000000..66e81a5 --- /dev/null +++ b/docs/features/fsd-entities-migration/tasks.md @@ -0,0 +1,53 @@ +# FSD Entities Migration — задачи + +Статус: запланировано + +Подробные шаги, команды и expected results находятся в [plan.md](plan.md). + +## Pre-flight + +- [ ] Feature branch создана: `codex/fsd-entities-migration` +- [ ] spec.md написана +- [ ] plan.md написан +- [ ] tasks.md создан +- [ ] Все тесты проходят на текущем состоянии + +## 1. entities/stock/ + +- [ ] Создать `entities/stock/api/stockApi.ts` с API-функциями stock +- [ ] Создать `entities/stock/model/useStock.ts`, `useStockCandles.ts`, `useStockDividends.ts` +- [ ] Создать `entities/stock/index.ts` barrel +- [ ] Переместить тесты в `entities/stock/model/` +- [ ] Удалить stock API из `shared/api/client.ts` и barrely +- [ ] Превратить `hooks/useStock*` в shim-файлы +- [ ] Переключить `StockPage.tsx` на новые импорты +- [ ] Тесты проходят + +## 2. entities/bond/ + +- [ ] Создать `entities/bond/api/bondApi.ts` с API-функциями bond +- [ ] Создать `entities/bond/model/useBond.ts`, `useBondCandles.ts` +- [ ] Создать `entities/bond/index.ts` barrel +- [ ] Переместить тесты в `entities/bond/model/` +- [ ] Удалить bond API из `shared/api/client.ts` и barrely +- [ ] Превратить `hooks/useBond*` в shim-файлы +- [ ] Переключить `BondPage.tsx` на новые импорты +- [ ] Тесты проходят + +## 3. entities/portfolio/ + +- [ ] Создать `entities/portfolio/api/portfolioApi.ts` +- [ ] Создать `entities/portfolio/model/` (5 хуков) +- [ ] Создать `entities/portfolio/index.ts` barrel +- [ ] Превратить `api/portfolio.ts` в shim +- [ ] Превратить `hooks/usePortfolio*` и `usePositionMutations` в shim-файлы +- [ ] Переключить страницы портфелей на новые импорты +- [ ] Build проходит + +## 4. Финальная проверка + +- [ ] Все тесты проходят +- [ ] Линт проходит +- [ ] Build проходит +- [ ] Только целевые изменения в diff +- [ ] Отмечено выполнение в tasks.md