docs(specs): archive frontend auth query gating change
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
# remove-frontend-hook-token-params — Design
|
||||
|
||||
## Context
|
||||
|
||||
Защищённые frontend hooks для investment-данных сейчас выставляют session
|
||||
`accessToken` как часть caller-facing interface только для вычисления React
|
||||
Query `enabled`. Сам `Authorization` header уже принадлежит
|
||||
`frontend/src/shared/api/client.ts`: `AuthProvider` конфигурирует shared client
|
||||
token/refresh/clear handlers, `authenticatedFetch` добавляет bearer headers для
|
||||
non-auth endpoints и выполняет существующий single retry после `UNAUTHORIZED`.
|
||||
|
||||
Повторяющееся token plumbing видно в entity hooks для Brokerage Accounts,
|
||||
Portfolio, Account Ledger и Future Cash Events, а также в их callers:
|
||||
accounts/account page ViewModels, account operations/events pages и breadcrumbs.
|
||||
Из-за этого page-level modules знают transport credential, который больше
|
||||
нигде не используют.
|
||||
|
||||
FSD-направление импортов важно для выбора seam. Entity hooks могут зависеть от
|
||||
`shared/api`, но не должны импортировать `features/auth`. Поэтому auth-aware
|
||||
query interface нельзя реализовать через прямой `useAuth()` внутри entity hooks.
|
||||
Design должен сохранить поток auth configuration из `features/auth` в
|
||||
`shared/api`, а entity hooks должны потреблять только `shared/api`.
|
||||
|
||||
Frontend-ограничения: React/Vite, TanStack Query, openapi-react-query,
|
||||
MSW-backed Vitest tests, strict TypeScript, dependency-cruiser FSD boundaries,
|
||||
canonical gate `npm ci && npm run verify` под Node 22. Backend routes,
|
||||
generated OpenAPI snapshot и user-visible behavior не меняются.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Убрать session token parameters из interfaces защищённых investment entity
|
||||
hooks.
|
||||
- Централизовать authenticated query enablement в `shared/api`, рядом с
|
||||
существующим authenticated transport seam.
|
||||
- Сохранить bearer injection, refresh/retry behavior, cache keys, stale
|
||||
policies, retry policies и page state behavior.
|
||||
- Сохранить валидное FSD-направление зависимостей: entity hooks зависят от
|
||||
shared API/auth query utilities, а не от auth feature.
|
||||
- Добавить focused regression proof, что protected queries остаются disabled
|
||||
без active session и загружаются нормально, когда session появляется.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Менять backend auth semantics, HTTP routes, DTOs, generated OpenAPI files или
|
||||
T-Bank behavior.
|
||||
- Переносить auth feature целиком в shared или менять login/register/logout UX.
|
||||
- Refactor `useTbankConnectionViewModel`; там есть похожий локальный
|
||||
`enabled: Boolean(accessToken)` pattern, но он находится вне четырёх entity
|
||||
hook interfaces, названных этим review item.
|
||||
- Перестраивать account page VM/view, account operations/events pages или
|
||||
breadcrumbs за пределами удаления token plumbing, нужного из-за изменения
|
||||
hook interfaces.
|
||||
- Консолидировать дублированные frontend error-code helpers.
|
||||
|
||||
## Decisions
|
||||
|
||||
**D1. Ввести shared authenticated-query module.** `shared/api` получает
|
||||
небольшой authenticated-query interface, который оборачивает TanStack `useQuery`
|
||||
и совмещает обычные query options вызывающего кода с session-aware enablement.
|
||||
Protected entity hooks вызывают этот interface вместо того, чтобы принимать
|
||||
`accessToken` и самостоятельно выставлять `enabled`. Endpoint-specific hooks
|
||||
по-прежнему владеют path/query parameters, cache keys, stale times и retry
|
||||
policy.
|
||||
|
||||
*Альтернатива:* оставить `accessToken` hook parameter и почистить только callers.
|
||||
Отклонено: shallow interface остаётся видимым, поэтому новые callers продолжат
|
||||
изучать token plumbing как обязательный pattern.
|
||||
|
||||
**D2. Auth state передаётся в shared API через внутренний snapshot, а не через
|
||||
импорт `useAuth`.** `features/auth` продолжает конфигурировать `shared/api`
|
||||
current token и refresh handlers. Shared API layer дополнительно публикует
|
||||
минимальный authenticated-session snapshot/subscription, который authenticated
|
||||
queries используют для вычисления `enabled`. Это сохраняет направление
|
||||
зависимостей `features -> shared` и `entities -> shared`, без импортов
|
||||
`entities -> features` или `shared -> features`.
|
||||
|
||||
*Альтернатива:* разрешить entity hooks вызывать `useAuth()`. Отклонено: entities
|
||||
начали бы зависеть вверх от feature slice и нарушили бы documented FSD
|
||||
direction.
|
||||
|
||||
**D3. Compose, а не replace, caller enablement.** Authenticated query
|
||||
enablement вычисляется как `hasSession && callerEnabled`, где `callerEnabled`
|
||||
по умолчанию равен `true`. Account-scoped hooks сохраняют domain precondition,
|
||||
передавая `Boolean(accountId)` как caller enablement. Это сохраняет текущее
|
||||
поведение: undefined account id не создаёт request, а unauthenticated state
|
||||
подавляет все protected investment requests.
|
||||
|
||||
*Альтернатива:* проверять в authenticated query только session и полагаться на
|
||||
empty path params при отсутствующем account id. Отклонено: это превратило бы
|
||||
текущую disabled-query ветку в невалидный protected request.
|
||||
|
||||
**D4. Transport retry остаётся независимым от query gating.** Authenticated
|
||||
query module только решает, может ли protected query стартовать. Он не строит
|
||||
headers, не запускает refresh напрямую и не интерпретирует `401` responses.
|
||||
Существующий `authenticatedFetch` остаётся единственным owner bearer injection и
|
||||
refresh/retry path для requests, которые уже стартовали.
|
||||
|
||||
**D5. Protected entity hook interfaces становятся domain-shaped.** Caller-facing
|
||||
interfaces после изменения описываются потребностями данных: accounts list не
|
||||
имеет arguments, portfolio/events принимают optional account id, operations
|
||||
принимает optional account id и query object. Page/ViewModel modules перестают
|
||||
читать `accessToken`, если он был нужен только для передачи в hook.
|
||||
|
||||
**D6. Tests покрывают seam, а не token plumbing.** Regression tests должны
|
||||
доказывать observable behavior через shared authenticated-query seam: protected
|
||||
query не попадает в MSW без configured session, и тот же hook загружается, когда
|
||||
session exists. Existing endpoint tests обновляются под новые interfaces и
|
||||
продолжают проверять query parameter normalization и successful endpoint paths.
|
||||
Tests не должны grep-ить source, чтобы доказать исчезновение token argument;
|
||||
TypeScript и компиляция call sites покрывают interface change.
|
||||
|
||||
## Delivery Map
|
||||
|
||||
| Area | Status | Architectural outcome or N/A reason |
|
||||
| --- | --- | --- |
|
||||
| frontend | `affected` | `shared/api` владеет authenticated query enablement; четыре protected investment entity hooks убирают `accessToken` parameters; callers обновлены на domain-shaped hook interfaces; focused tests покрывают no-session gating и session-enabled loading. |
|
||||
| backend | `N/A` | Backend auth, routes, DTOs, T-Bank integration и runtime OpenAPI не меняются. |
|
||||
| specs | `affected` | Этот SDD change хранит approved Proposal, skipped Delta Specs, Design и Tasks для behavior-preserving frontend architecture cleanup. |
|
||||
| docs | `N/A` | As-built product docs не меняются, потому что user-visible behavior и system contract остаются прежними; frontend-local guidance уже фиксирует, что bearer headers принадлежат shared authenticated fetch. |
|
||||
|
||||
### Contract Gate Decision
|
||||
|
||||
| Gate | Status | Reason |
|
||||
| --- | --- | --- |
|
||||
| runtime-openapi | `N/A` | Change не затрагивает backend или frontend/backend API contract, generated client или OpenAPI snapshot. |
|
||||
|
||||
### Solution Assessment
|
||||
|
||||
Формальная build-versus-buy assessment не triggered. Change не добавляет и не
|
||||
заменяет authentication, security, storage, external integration,
|
||||
observability, caching, concurrency control или infrastructure. Он сохраняет
|
||||
существующий auth mechanism и TanStack Query/openapi-react-query stack,
|
||||
перенося project-owned query enablement за меньший in-process interface.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Session reactivity regression.** Если shared auth snapshot не уведомляет
|
||||
authenticated queries при изменении token state, queries могут остаться
|
||||
disabled после login/refresh или enabled после logout. Митигация:
|
||||
реализовать snapshot как subscribed external store и покрыть
|
||||
disabled/enabled transition behavior tests.
|
||||
- **FSD boundary regression.** Чтение auth state из entity hooks через
|
||||
`features/auth` исправило бы симптом, но нарушило architecture. Митигация:
|
||||
разместить seam в `shared/api` и опираться на существующий dependency-cruiser
|
||||
gate.
|
||||
- **Over-wide shared API interface.** Helper, который зеркалит все TanStack
|
||||
Query options, может стать shallow. Митигация: держать его узко
|
||||
ответственным за composition обычных `useQuery` options с authenticated
|
||||
enablement; endpoint hooks сохраняют endpoint-specific policy.
|
||||
- **Похожий pattern остаётся в T-Bank connection VM.** Change намеренно
|
||||
оставляет один feature-local use of `accessToken` gating без изменений.
|
||||
Митигация: зафиксировать это как out of scope; отдельный будущий change
|
||||
может решить, должны ли feature-level status queries тоже переехать на shared
|
||||
authenticated-query seam.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Добавить shared authenticated-query module и tests для no-session disabled
|
||||
behavior и session-enabled loading.
|
||||
2. Refactor Brokerage Accounts, Portfolio, Account Ledger и Future Cash Events
|
||||
entity hooks на shared authenticated-query interface, сохранив query keys,
|
||||
request parameters, stale times и retry policies.
|
||||
3. Обновить все callers и tests на новые domain-shaped hook interfaces, убрав
|
||||
`useAuth()` reads, которые существовали только для передачи `accessToken`.
|
||||
4. Запустить focused frontend tests для изменённых hooks/callers, затем
|
||||
frontend canonical gate `npm run verify`.
|
||||
5. Запустить `openspec validate remove-frontend-hook-token-params --type change
|
||||
--strict --store wealth-ledger-specs`, выполнить semantic verification
|
||||
против Proposal/Design/Tasks и использовать workspace dispatcher перед
|
||||
archive.
|
||||
|
||||
Откат — обычный frontend/specs code revert. Data migration, OpenAPI sync,
|
||||
backend migration и feature flag не вводятся.
|
||||
|
||||
## Open Questions
|
||||
|
||||
<!-- none -->
|
||||
Reference in New Issue
Block a user