Files
specs/openspec/changes/archive/2026-08-13-add-brokerage-accounts/design.md
T
2026-08-13 20:59:38 +03:00

66 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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 не требуется.