66 lines
9.7 KiB
Markdown
66 lines
9.7 KiB
Markdown
## Context
|
||
|
||
См. мотивацию в `proposal.md` и поведение в `specs/brokerage-accounts/spec.md`. Backend уже хранит один зашифрованный T-Bank token на User, проверяет его через `UsersService/GetAccounts` и применяет общую policy TLS, rate limit, deadline и retry. Этот RPC пока отбрасывает response body, а `/accounts` во frontend является placeholder-страницей. OpenAPI backend остаётся источником transport contract для frontend.
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
|
||
- Использовать существующую T-Bank boundary и resilience policy для user-scoped read-path списка счетов.
|
||
- Отделить публичную модель инвестиционного счёта от protobuf enum и банковских типов T-Bank.
|
||
- Дать `/accounts` понятные loading, empty, error и loaded состояния в FSD-структуре, сохранив стиль codebase и визуальный язык `references/fin-tracker` без переноса его кода или неподдерживаемых данных.
|
||
- Зафиксировать feature-документацию с пользовательским потоком, API и MVP-ограничениями после реализации.
|
||
|
||
**Non-Goals:**
|
||
|
||
- Суммы, доходность, агрегация, графики, структура портфеля и любые вычисления по счетам.
|
||
- Кэш backend, timestamp последнего обновления, ручная синхронизация или частичные результаты.
|
||
- Закрытые, банковские и неизвестные типы счетов, операции и изменение T-Bank connection из списка.
|
||
|
||
## Decisions
|
||
|
||
### Read-path списка и public contract
|
||
|
||
Новый защищённый `GET /api/v1/brokerage-accounts` сначала читает `TbankConnection` User. При её отсутствии он немедленно возвращает `409 TBANK_NOT_CONNECTED`; gRPC не вызывается. При connection сервис расшифровывает token только для вызова T-Bank и получает открытые счета через существующий общий client, поэтому к списку применяются те же TLS, limiter, deadline и retry, что и к проверке token.
|
||
|
||
Успех возвращает прямой JSON-массив, а не envelope: сейчас ответ не содержит метаданных, и обёртка не несёт семантики. Ошибка не возвращает данные. При gRPC `UNAUTHENTICATED` и `PERMISSION_DENIED` connection не удаляется, а клиент получает `400 INVALID_TBANK_TOKEN`; другие ошибки остаются `503 TBANK_UNAVAILABLE`.
|
||
|
||
Альтернативы: использовать `/accounts` для API смешало бы инвестиционные и банковские счета; статус `200` без connection сделал бы обязательную интеграцию неявным UI-флагом; сохранение или возврат старого списка потребовало бы cache invalidation и вводило бы пользователя в заблуждение об актуальности.
|
||
|
||
### Нормализация Brokerage Account
|
||
|
||
Backend добавляет нормализованный DTO `BrokerageAccount` с `id`, `name`, `type` и `openedOn`. Трансляция включает только upstream-типы `ACCOUNT_TYPE_TINKOFF`, `ACCOUNT_TYPE_TINKOFF_IIS`, `ACCOUNT_TYPE_INVEST_BOX` и `ACCOUNT_TYPE_INVEST_FUND`, преобразуя их соответственно в `BROKERAGE`, `INDIVIDUAL_INVESTMENT`, `INVEST_BOX` и `MONEY_MARKET_FUND`. Остальные и неизвестные типы отфильтровываются, порядок upstream сохраняется.
|
||
|
||
`name` нормализуется через trim и при пустом результате заменяется fallback названием типа. Timestamp `openedDate` переводится в nullable `YYYY-MM-DD`; время намеренно не публикуется, потому что экран показывает календарную дату открытия, а не момент события.
|
||
|
||
Альтернативы: передача protobuf enum привязала бы frontend к T-Bank contract; показ неизвестных типов как инвестиционных расширил бы доменную границу без решения пользователя; отображение технического id вместо fallback-названия ухудшило бы интерфейс без пользы для MVP.
|
||
|
||
### UI и React Query
|
||
|
||
Новый entity slice `brokerage-account` инкапсулирует typed API query, локальные transport aliases из generated `paths` и display helpers. `AccountsPage` использует entity через public barrel и выбирает одно из состояний: loading, known-error, generic error, empty либо loaded. Query имеет `staleTime: 60_000` и отключённый automatic retry; отключённый focus refetch сохраняется глобальной настройкой. Свежий React Query cache исключает повторный GET в течение минуты; после его устаревания `isFetching` заменяет прежний список loading-state. Retry для временной и неизвестной ошибки вызывает `refetch`; кнопка настроек ведёт на `/settings`.
|
||
|
||
Успешная замена или удаление connection инвалидирует query списка. Карточки реализуются Mantine-компонентами и локальным CSS module: белая поверхность, тонкая slate-граница, умеренная мягкая тень, синий accent/kicker и заметный hover/focus. Сетка адаптивна: две колонки при доступной ширине и одна на узком экране. Карточка является доступной ссылкой на `/accounts/:accountId`, показывает имя, локализованный тип и при наличии дату открытия. Не добавляются hero, sparkline и финансовые метрики, поскольку текущий контракт их не предоставляет.
|
||
|
||
Альтернативы: размещение query прямо в page смешало бы transport и presentation; копирование разметки или mock-метрик FinTracker нарушило бы границы references и исказило бы реальные данные; фиксированная двухколоночная mobile-сетка ухудшила бы читаемость.
|
||
|
||
### Документация и проверка
|
||
|
||
После реализации `docs/features/brokerage-accounts.md` описывает пользовательский поток, endpoint, коды ошибок, нормализованные типы и отсутствие кэша/времени актуальности; последовательность запроса фиксируется Mermaid-диаграммой. Постоянный ADR не требуется: backend boundary, OpenAPI и React Query policy уже закреплены ADR 003, 005 и 007, а новая фильтрация типов является ограниченной feature-деталью.
|
||
|
||
## Risks / Trade-offs
|
||
|
||
- [Токен может быть отозван после подключения] → Список возвращает `INVALID_TBANK_TOKEN` и ведёт в настройки, но не удаляет connection автоматически.
|
||
- [Данные меняются у T-Bank между загрузками] → Frontend использует результат до 60 секунд, но backend не хранит кэш и не передаёт метку свежести.
|
||
- [T-Bank добавит новый инвестиционный тип] → Неизвестный тип не показывается до явного расширения доменной модели.
|
||
- [Визуальный референс содержит финансовые показатели, которых нет в API] → Сохраняются лишь стилистические признаки, не генерируются placeholder-значения.
|
||
|
||
## Migration Plan
|
||
|
||
1. Расширить T-Bank client получением response body и добавить user-scoped сервис, controller и OpenAPI DTO списка.
|
||
2. Добавить backend unit/e2e проверки нормализации, отсутствующего connection, error mapping и auth.
|
||
3. Перегенерировать frontend API client из работающего backend OpenAPI.
|
||
4. Добавить entity, query, account cards и все состояния `/accounts`; инвалидировать query из T-Bank connection feature.
|
||
5. Проверить backend и frontend quality gates, визуально проверить desktop/mobile и создать `docs/features/brokerage-accounts.md`.
|
||
|
||
Rollback до release удаляет endpoint и frontend-срез; схема базы данных не изменяется. Удалять `TbankConnection` или шифротекст при rollback не требуется.
|