5.7 KiB
Raw Blame History

FSD Frontend Refactor

Цель

Довести фронтенд-архитектуру до эталонного Feature-Sliced Design (FSD) — устранить существующие нарушения, вынести жирные страницы в виджеты/фичи, внедрить автоматический контроль FSD-правил.

Текущее состояние: 4.2/5 по FSD-зрелости. Цель: 5/5.

Задачи

1. Исправить нарушение слоёв в shared/lib/test

  • shared/lib/test/test-utils.tsx импортирует SessionProvider из @/app/providers
  • Это единственное нарушение FSD (shared → app)
  • Решение: вынести TestSessionProvider в shared/lib/test/ и переключить test-utils.tsx на него
  • Тесты не должны сломаться: TestSessionProvider предоставляет те же значения контекста (замоканые)

2. Вынести BrokerPositionTable из страницы в виджет

  • pages/broker-positions/ui/BrokerPositionsPage.tsx (310 строк) содержит внутренние компоненты:
    • BrokerPositionTable (~190 строк) — таблица с пагинацией, skeleton, inline-стилями
    • PositionTicker (~20 строк) — ссылка на инструмент
    • formatQuantity — хелпер
  • Решение: создать widgets/broker-positions-table/ с:
    • ui/BrokerPositionTable.tsx
    • ui/PositionTicker.tsx
    • index.ts
  • Страница сокращается до ~100 строк (только cursor-логика + композиция)

3. Вынести BrokerSummary, BrokerAssetCards, BrokerOverviewSkeleton в виджет

  • pages/broker-account/ui/BrokerAccountOverviewPage.tsx (160 строк) содержит:
    • BrokerSummary — сводка портфеля (карточки стоимости и денег)
    • BrokerAssetCards — карточки классов активов (акции/облигации)
    • BrokerOverviewSkeleton — скелетон загрузки
    • allocationPercent, formatAllocationPercent — хелперы
  • Решение: создать widgets/broker-overview/ с:
    • ui/BrokerSummary.tsx
    • ui/BrokerAssetCards.tsx
    • ui/BrokerOverviewSkeleton.tsx
    • index.ts
  • Страница сокращается до ~40 строк (только loading/error guard + композиция)

4. Вынести AddPositionForm в фичу

  • pages/portfolios/ui/PortfolioDetailPage.tsx (290 строк) содержит inline-форму добавления позиции:
    • 4 state-переменные (newSecid, newQty, newPrice, newDate)
    • 4 input-поля с inline-стилями
    • Валидация и submit
  • Это полноценная бизнес-фича, не место в странице
  • Решение: создать features/add-position/ с:
    • api/useAddPosition.ts — мутация (обёртка над usePositionMutations)
    • model/useAddPositionForm.ts — управление формой
    • ui/AddPositionForm.tsx — UI формы
    • index.ts
  • Страница сокращается до ~140 строк (showAddForm toggle + <AddPositionForm portfolioId={id} />)

5. Вынести cursor-пагинацию в shared-хук

  • 25 строк cursor-логики дублируются в:
    • pages/broker-positions/ui/BrokerPositionsPage.tsx
    • pages/broker-operations/ui/BrokerOperationsPage.tsx
  • Решение: создать shared/lib/useCursorPagination.ts
function useCursorPagination() {
  // cursor, pageNumber, handleNext(cursor), handlePrevious, reset
}

6. Добавить ESLint-плагин FSD

  • Текущий import/no-restricted-paths проверяет только межслойные границы
  • Плагин @conarti/eslint-plugin-feature-sliced добавит:
    • Проверки public API (запрет импорта из внутренних модулей в обход index.ts)
    • Проверки сегментов (api/ui/model не импортируют друг друга напрямую)
  • Решение: установить плагин и включить recommended rules

Критерии приёмки (Acceptance Criteria)

  • AC1: test-utils.tsx не импортирует из @/app/*
  • AC2: widgets/broker-positions-table создан, страница использует его
  • AC3: widgets/broker-overview создан, страница использует его
  • AC4: features/add-position создана, страница использует её
  • AC5: shared/lib/useCursorPagination.ts создан, обе страницы используют его
  • AC6: @conarti/eslint-plugin-feature-sliced установлен и настроен
  • AC7: npm run lint проходит без ошибок
  • AC8: npm run build проходит без ошибок
  • AC9: Все существующие тесты проходят (включая тесты рефакторимых компонентов)
  • AC10: Визуально поведение страниц не изменилось

Ограничения

  • Не менять API-контракты компонентов (пропсы, типы). Только перемещение кода.
  • Не добавлять новую функциональность — только рефакторинг.
  • inline-стили переносятся вместе с компонентами; централизация UI-кита — отдельная задача.