docs(specs): archive brokerage account list change
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
## Context
|
||||
|
||||
`GET /api/v1/brokerage-accounts` уже разрешает текущее T-Bank connection и
|
||||
поддерживаемые счета через `TbankAccessService`, сохраняя upstream-порядок и
|
||||
существующее отображение публичных ошибок. Получение Portfolio использует ту
|
||||
же границу доступа и рассчитывает точные итоги по каждой валюте из значений
|
||||
позиций `GetPortfolio` и денежных остатков `GetPositions`. Текущая
|
||||
нормализация также обогащает позиции для экрана деталей счёта, что не нужно
|
||||
для сводки списка счетов.
|
||||
|
||||
Frontend уже потребляет сгенерированный OpenAPI-тип через один
|
||||
аутентифицированный React Query hook с 60-секундной политикой актуальности. Его
|
||||
page VM сохраняет успешный query snapshot во время фонового refetch и выбирает
|
||||
известные error-состояния списка. Сейчас entity рендерит двухколоночную
|
||||
коллекцию ссылок только с метаданными счета. Новый контракт должен сохранить
|
||||
существующую семантику connection, query, навигации и клавиатуры, обогащая
|
||||
каждый элемент без представления разных валют как единого значения.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Расширить существующий контракт списка явной nullable-сводкой Portfolio для
|
||||
каждого счёта, сохранив policy доступа к счетам и upstream-порядок.
|
||||
- Переиспользовать точный сохраняющий валюты расчёт итогов Portfolio и для
|
||||
snapshot деталей, и для сводок списка без работы с метаданными позиций.
|
||||
- Локализовать временный upstream-сбой на сводке затронутого счёта, сохранив
|
||||
установленную семантику ошибок connection и token на уровне списка.
|
||||
- Сохранить существующий 60-секундный React Query-кэш и policy Client Cached
|
||||
Snapshot, выражая состояния сводки через текущую границу page VM и
|
||||
presentation.
|
||||
- Обновить as-built документацию только после проверки поставленного поведения.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- FX-conversion, межвалютный или межсчётный агрегат, отдельное отображение Cash
|
||||
Balance, сортировка/фильтрация/настройки списка или постоянное хранение
|
||||
Portfolio.
|
||||
- Переработка Portfolio на странице деталей счёта, управления T-Bank
|
||||
connection, resilience policy T-Bank client или глобальной frontend fetch
|
||||
policy.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1. Обогатить каждый нормализованный счёт nullable-сводкой на существующей границе списка
|
||||
|
||||
`TbankAccessService` продолжит владеть разрешением current connection, policy
|
||||
поддерживаемых счетов, обработкой token и начальным вызовом `GetAccounts`.
|
||||
Поток списка сделает разрешённый token и ID поддерживаемых upstream-счетов
|
||||
доступными `BrokerageAccountsService`, который сохранит ответственность за
|
||||
формирование ответа списка. Для каждого поддерживаемого счёта он параллельно
|
||||
запросит два upstream-входа Portfolio и сохранит нормализованный счёт на его
|
||||
исходной позиции.
|
||||
|
||||
Работа по каждому счёту будет собрана без изменения порядка счетов. Результат
|
||||
`TBANK_UNAVAILABLE` любого из входов сводки создаёт `portfolioSummary: null`
|
||||
только для этого счёта. Начальное разрешение connection, обнаружение счетов,
|
||||
перевод invalid-token и непредвиденные ошибки сохраняют текущее поведение
|
||||
Problem Details на уровне списка; они не превращаются молча в частичный успех.
|
||||
|
||||
*Альтернатива:* вызывать public Portfolio service страницы деталей для каждого
|
||||
счёта. Отклонено, поскольку это свяжет список с cache snapshot деталей,
|
||||
семантикой timestamp и stale-fallback и вызовет нерелевантное обогащение
|
||||
метаданных Instrument.
|
||||
|
||||
*Альтернатива:* делать любую неуспешную сводку ошибкой списка. Отклонено,
|
||||
поскольку это противоречит требуемому поведению частичной доступности и
|
||||
скрывает в ином случае доступную навигацию к счёту.
|
||||
|
||||
### D2. Извлечь общий normalizer точных итогов, а не вторую модель Portfolio
|
||||
|
||||
Небольшой backend-owned normalizer примет upstream-значения позиций и денежные
|
||||
остатки и вернёт итоги `Money[]`, сгруппированные по нормализованной исходной
|
||||
валюте. Он сохранит существующую decimal-арифметику и правила включения:
|
||||
нулевые значения или значения без валюты исключаются; валидные текущие
|
||||
значения позиций и Cash Balance в одной валюте точно складываются;
|
||||
FX-conversion не выполняется. Portfolio service деталей использует helper при
|
||||
формировании существующего snapshot, а поток списка обернёт тот же результат
|
||||
как `{ totalsByCurrency }`.
|
||||
|
||||
Это сохраняет денежный инвариант в одном месте и устраняет зависимость от
|
||||
маппинга позиций Portfolio, запросов метаданных, timestamp или cache-поведения.
|
||||
Пустой успешный upstream Portfolio становится пустым массивом итогов и,
|
||||
следовательно, доступной non-null сводкой списка.
|
||||
|
||||
*Альтернатива:* дублировать текущий aggregation loop в service списка.
|
||||
Отклонено, поскольку две реализации точных денежных расчётов могут разойтись
|
||||
на edge cases денежных остатков или валют.
|
||||
|
||||
*Альтернатива:* возвращать в списке полный `PortfolioDto`. Отклонено, поскольку
|
||||
контракт списка не требует ни позиций, ни метаданных snapshot/cache и раскроет
|
||||
избыточно дорогой payload.
|
||||
|
||||
### D3. Сделать OpenAPI DTO и generated client единственным транспортным изменением
|
||||
|
||||
`BrokerageAccountDto` получает nullable `portfolioSummary`, единственное поле
|
||||
которой — `totalsByCurrency: MoneyDto[]`. Swagger/OpenAPI описывает эту форму,
|
||||
после чего frontend snapshot и generated types обновляются установленным
|
||||
workflow runtime contract. Frontend-код продолжает выводить тип счёта из
|
||||
сгенерированных `paths`, не создавая параллельный transport interface.
|
||||
|
||||
Это совместимое аддитивное изменение ответа, но оно остаётся изменением
|
||||
публичного контракта и требует cross-repository gate runtime/OpenAPI.
|
||||
|
||||
### D4. Сохранить query-поведение списка; подготовить представление карточек в page VM
|
||||
|
||||
Существующие query key brokerage-account, 60-секундный `staleTime`, policy без
|
||||
retry и invalidation после замены/удаления T-Bank connection не изменяются.
|
||||
Accounts page VM сохранит текущее top-level выделение loading/error/empty/loaded
|
||||
и поведение cached snapshot. Он подготовит ограниченную display model каждой
|
||||
карточки из generated account: не более двух итогов исходных валют и явный
|
||||
счётчик/индикатор остальных валют либо раздельные локальные состояния
|
||||
недоступной и пустой сводки.
|
||||
|
||||
Presentation component списка счетов станет одноколоночной последовательностью
|
||||
широких карточек-ссылок. Каждая ссылка сохраняет навигацию по всей карточке и
|
||||
native keyboard focus и показывает локализованные тип, имя, сводку, вторичные
|
||||
метаданные даты открытия и видимое действие открытия. Presentation-код
|
||||
форматирует одно переданное значение Money существующим shared formatter; он
|
||||
не агрегирует и не конвертирует значения.
|
||||
|
||||
*Альтернатива:* рассчитывать видимые два итога в entity view. Отклонено,
|
||||
поскольку выбор и подсчёт значений из коллекции — derived presentation data,
|
||||
принадлежащие page VM согласно frontend VM boundary.
|
||||
|
||||
*Альтернатива:* ввести новый client cache или независимо получать Portfolio
|
||||
каждого счёта. Отклонено, поскольку обогащённый endpoint списка уже передаёт
|
||||
нужное компактное наблюдение, а отдельные query добавят fan-out, loading states
|
||||
и пути invalidation.
|
||||
|
||||
## Delivery Map
|
||||
|
||||
| Area | Status | Architectural outcome or N/A reason |
|
||||
| --- | --- | --- |
|
||||
| frontend | `affected` | Regenerated list response type, существующие query policy/invalidation, page VM card display models и доступные одноколоночные summary cards реализуют обогащённые состояния списка. |
|
||||
| backend | `affected` | Существующая account access boundary поставляет поддерживаемые счета; общий normalizer точных итогов Portfolio и orchestration списка формируют сводки по каждому счёту с локальной обработкой upstream-недоступности и аддитивным DTO. |
|
||||
| specs | `affected` | Этот change хранит утверждённые Proposal, Delta Specs, Design и последующие implementation Tasks в canonical store. |
|
||||
| docs | `affected` | После поставки as-built Brokerage Accounts и релевантная system documentation описывают фактический обогащённый контракт списка, частичную доступность сводки и неизменную границу наблюдения current connection. |
|
||||
|
||||
### Contract Gate Decision
|
||||
|
||||
| Gate | Status | Reason |
|
||||
| --- | --- | --- |
|
||||
| runtime-openapi | `required` | Backend изменяет публичный ответ списка, а frontend потребляет его generated OpenAPI snapshot. |
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Upstream fan-out растёт с количеством поддерживаемых счетов.** Список
|
||||
выполняет два Portfolio RPC на счёт, но использует существующие client-wide
|
||||
rate limiter, deadline и retry policy вместо создания другого механизма
|
||||
concurrency. Временно недоступная отдельная сводка деградирует локально.
|
||||
- **Расхождение агрегатов сводки и деталей.** Общая точная нормализация и
|
||||
фокусированные money tests исключают разную обработку Cash Balance, нулевых
|
||||
сумм и исходных валют.
|
||||
- **Частичный ответ может скрыть невалидные учётные данные.** Локальным для
|
||||
сводки является только известный результат недоступности; token и другие
|
||||
сбои уровня списка проходят через существующий error translation.
|
||||
- **Широкие карточки могут стать хуже для сканирования на малых экранах.**
|
||||
Одноколоночный layout сохраняет компактные вторичные метаданные, ограничивает
|
||||
отображаемые валюты двумя и явно обозначает пропущенные.
|
||||
- **Кэшированные данные могут быть временно устаревшими.** Это сохраняет
|
||||
установленный trade-off 60-секундного Client Cached Snapshot: фоновое
|
||||
обновление и его предупреждение остаются видимыми, а известные данные не
|
||||
очищаются.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Добавить компактный summary DTO и разделить точную нормализацию итогов
|
||||
Portfolio между существующим потоком деталей и обогащённым потоком списка
|
||||
счетов.
|
||||
2. Расширить orchestration списка для получения сводок внутри существующей
|
||||
T-Bank access и resilience boundary, сохранив порядок счетов и отображая в
|
||||
`null` только недоступность отдельного счёта.
|
||||
3. Перегенерировать frontend OpenAPI snapshot/types из работающего backend и
|
||||
пройти runtime contract gate.
|
||||
4. Адаптировать существующие consumer account query, page VM, карточки,
|
||||
переводы и risk-based tests, сохранив loading, error, empty, cache и
|
||||
invalidation behavior.
|
||||
5. После проверки реализации обновить as-built feature/system documentation
|
||||
до поставленных контракта и поведения.
|
||||
|
||||
### Rollback Contract
|
||||
|
||||
| Field | Decision |
|
||||
| --- | --- |
|
||||
| Status | `required` |
|
||||
| Reason | Аддитивное поле ответа — публичный API contract, потребляемый frontend. |
|
||||
| Trigger | Release-blocking defect в доступности списка счетов, точности денежной сводки или совместимости с generated client. |
|
||||
| Actions | Одновременно откатить frontend consumption и backend additive summary/DTO; перед выпуском откатываемой пары перегенерировать и закоммитить соответствующий frontend OpenAPI snapshot. |
|
||||
| Data safety | Schema, persisted Portfolio, Brokerage Account или данные владения не создаются и не мутируют; rollback лишь убирает из ответа текущее upstream-наблюдение. |
|
||||
| Verification | Запустить canonical gates затронутых backend и frontend, а также runtime OpenAPI gate против откатанного контракта; проверить, что список сохраняет прежние метаданные, навигацию и error states. |
|
||||
|
||||
## Open Questions
|
||||
|
||||
<!-- none -->
|
||||
Reference in New Issue
Block a user