refactor: align frontend ownership boundaries

Close audit gaps across API drift, query policy, UI, and tests.
This commit is contained in:
2026-09-06 18:20:05 +03:00
parent 175727bc93
commit c97f62bd98
118 changed files with 2190 additions and 2254 deletions
+3 -1
View File
@@ -2,7 +2,9 @@
OpenAPI backend является источником истины. После изменения backend-контракта committed snapshot `src/shared/api/spec/openapi.json` обновляют простым копированием опубликованного документа backend, а затем регенерируют `src/shared/api/generated/schema.d.ts` командой `npm run openapi:generate`. Оба файла committed; generated файл не редактируется вручную.
`npm run openapi:generate` использует committed snapshot offline. `verify` никогда не требует backend и не пытается автоматически синхронизировать OpenAPI artifacts.
`npm run openapi:generate` использует committed snapshot offline. `verify`
повторно генерирует types во временную директорию и сравнивает их с committed
`schema.d.ts`; backend для этого не требуется, а tracked files gate не меняет.
Все HTTP-вызовы используют `apiClient` и `apiQueryClient` из `src/shared/api`. Прямой `fetch` в features, pages и entities не используется. Query options берутся из `apiQueryClient`, а сохранённые cache keys и policies задаются вызывающим slice.
+8 -11
View File
@@ -9,9 +9,8 @@
- Публичный импорт слайса идёт через его `index.ts`.
- Тесты лежат рядом с исходником как `*.test.tsx`.
- Зависимости используют exact versions; не добавлять semver ranges.
- Главный `*.view.tsx` страницы не содержит несколько state-компонентов:
каждый state/section — отдельный `*.view.tsx` в `components/` (см. File roles).
- Полный локальный gate: `npm run verify`. Pre-commit запускает staged formatting, OpenAPI check и FSD dependency checks; commit message должен следовать Conventional Commits.
- Полный локальный gate: `npm run verify`. Pre-commit запускает staged formatting
и FSD dependency checks; commit message должен следовать Conventional Commits.
## React component style
@@ -39,11 +38,9 @@ Mandatory review conventions:
- `Component.types.ts` - props и локальные типы.
- `Component.module.css` - локальные стили.
State-компоненты страницы не колоцируются в одном `*.view.tsx`. Каждый
top-level state (loading, error, empty, loaded) и каждый именуемый section
выносится в отдельный `*.view.tsx` в подпапку `components/`. Имя файла
отражает state/section: `AccountOperationsPageLoaded.view.tsx`,
`AccountOperationsPageLoading.view.tsx`, `AccountOperationsPageError.view.tsx`.
Контейнер (`AccountOperationsPage.tsx`) импортирует их из `./components` и
выбирает нужный по `viewModel.state`. Нельзя держать несколько state components
внутри одного файла view — это mandatory review convention.
Самостоятельный top-level state или именуемый section обычно выносится в
соседний `*.view.tsx`, когда у него есть отдельная responsibility, reuse либо
change cadence или он скрывает principal component. Небольшой tightly-coupled
renderer остаётся рядом с parent. Предпочтительный flat pattern —
`components/AccountOperationsPageLoaded.view.tsx` без отдельной директории,
barrel, test и stylesheet для каждого компонента.
+5 -6
View File
@@ -27,12 +27,11 @@ image state. Простое форматирование одного перед
существующий formatter остаётся presentation concern; вычисления из всей
коллекции остаются во VM.
View files дробятся по смысловой нагрузке, а не только по количеству строк.
Каждый top-level state и каждый именуемый section страницы — отдельный
`*.view.tsx` в `components/`, а не накопление внутри одного большого
`*.view.tsx`. Контейнер импортирует state views из `./components` и выбирает
нужный по `viewModel.state`. Держать loading/error/empty/loaded components в
одном файле view нельзя.
View files дробятся по ownership и смысловой нагрузке, а не по количеству строк.
Самостоятельные top-level states и sections выносятся в flat `components/*.view.tsx`,
если это делает principal component и границы изменений яснее. Маленькие
tightly-coupled renderers остаются рядом с parent; механическое правило «один
component — один file» и line-count gate не используются.
Known API errors, retry и stale snapshot остаются local views, а непойманные
route errors обрабатывает общий route-keyed ErrorBoundary.