Files

164 lines
11 KiB
Markdown
Raw Permalink 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
Поведенческий contract зафиксирован в delta specs. Текущее состояние уже близко
к целевой модели, но терминология и один public API fragment расходятся:
Portfolio и Ledger используют Money как `{ currency, value }`, а Future Cash
Event возвращает прогнозную сумму двумя nullable полями. Docs и specs местами
говорят о счёте как о принадлежащем User, хотя runtime проверяет доступность
через текущий T-Bank connection и не хранит Brokerage Account как durable
aggregate.
Backend уже имеет общий Money DTO для Portfolio и Account Ledger, нормализацию
денежных значений, проверку account access через T-Bank connection и generated
OpenAPI как источник frontend types. Frontend уже умеет форматировать Money
через portfolio display helper, но страницы будущих событий и overview читают
`amount`/`currency` напрямую. Docs содержит canonical domain glossary, feature
docs и system overview, которые должны описывать только as-built состояние
после реализации.
Ограничение SDD: этот change меняет публичный контракт финансовой read-модели,
поэтому реализация должна синхронизировать backend runtime OpenAPI, generated
frontend types, frontend consumers, tests, specs и docs в одном delivery.
## Goals / Non-Goals
**Goals:**
- Сделать Future Cash Event прогнозную сумму единым nullable Money value во
всём runtime contract, generated contract и frontend rendering.
- Закрепить единый доменный язык для доступности Brokerage Account через
текущий T-Bank connection без durable ownership claim.
- Явно различить Portfolio Snapshot, Portfolio Position, Currency Instrument
Position и Cash Balance в specs, docs и проверяемом поведении.
- Расширить Instrument language на Future Cash Events без изменения смысла
существующих инструментальных identifiers.
- Обновить glossary и feature docs только после реализации, чтобы они описали
фактическое поведение, а не план.
**Non-Goals:**
- Не переименовывать публичные routes, пользовательские страницы или legacy
controller/service names только ради терминологии.
- Не вводить storage для Brokerage Account, Portfolio, Portfolio Snapshot,
Ledger Entry или Future Cash Event.
- Не менять supported account type policy, T-Bank token lifecycle, upstream
data sources, cache TTL, stale fallback или сортировку событий.
- Не выполнять широкую модульную перестройку NestJS/React сверх мест,
необходимых для contract и terminology alignment.
## Decisions
### D1. Сделать Future Cash Event amount единственным nullable Money field
Backend Upcoming Event DTO должен перейти с пары `amount`/`currency` на одно
nullable Money-поле для прогнозной суммы. Coupon, dividend и maturity получают
Money в исходной валюте, когда сумма известна; offer и неизвестные денежные
условия получают `null`. Расчёт количества, даты, event type, identifiers,
sorting и `calculatedAt` сохраняются.
Rationale: это убирает частный контракт, который противоречит Money в Portfolio
и Ledger, и делает неизвестность явной: нет суммы целиком, а не отдельно нет
amount или currency. Alternative оставить два поля и описать их как legacy
отклонена, потому что она закрепляет второй денежный язык и заставляет
frontend каждый раз реконструировать Money вручную.
### D2. Переиспользовать существующую Money normalization surface
Design не вводит новую денежную библиотеку или второй DTO. Upcoming Events
использует тот же shape и правила десятичных строк, что Portfolio и Account
Ledger: ISO currency, decimal string, no exponential notation, missing value as
`null`. Tests должны покрыть переходные edge cases: рассчитанная сумма,
нулевая/отсутствующая сумма offer, отрицательная или дробная normalization
там, где она проходит через общий money formatting path.
Alternative отдельный `ProjectedAmount` shape отклонена: доменно это тот же
Money, только nullable по uncertainty события.
### D3. Оставить route/UI names стабильными, уточнив domain language
Публичные routes и пользовательские entrypoints остаются `brokerage-accounts`,
Portfolio page и «Будущие события». Термины Portfolio Snapshot, Currency
Instrument Position и Cash Balance закрепляются в requirements, glossary,
feature docs и test naming там, где они объясняют поведение. Runtime class/file
names можно менять только если это локально улучшает читаемость без широкой
цепочки churn.
Rationale: пользователю и API consumers не нужна массовая переадресация из-за
уточнения модели; ценность change в согласованном смысле и денежном contract.
### D4. Считать account access текущим наблюдаемым правом через T-Bank connection
Implementation не должна добавлять ownership state. Все account-scoped flows
продолжают проверять, что account открыт, поддержан и доступен через текущий
T-Bank connection User на момент запроса. Specs/docs/client copy используют
этот язык; code tests защищают existing observable behavior, если затронутая
реализация меняется.
Alternative хранить список счетов как owned accounts отклонена: это расширяет
domain и storage без требования продукта и противоречит текущей брокерской
интеграции.
### D5. Frontend consumes generated Money contract directly
После backend contract change generated OpenAPI types синхронизируются штатным
workflow. Frontend pages, account overview block, event entity tests и MSW
fixtures переходят на nullable Money-поле и используют существующий
`formatMoney` без ручной сборки `{ currency, value }`. UI copy «без суммы»
сохраняется для `null`, а known Money отображается в исходной валюте.
Alternative временно поддерживать оба shapes во frontend отклонена для этого
локального продукта: это усложняет presentation path без совместимого backend
режима и оставляет двусмысленность в tests.
## Delivery Map
| Area | Status | Architectural outcome or N/A reason |
| --- | --- | --- |
| frontend | `affected` | Generated API types, event fixtures, account overview and full events page consume nullable Money for projected event amount and preserve existing event UI behavior. |
| backend | `affected` | Upcoming Events runtime DTO/service/tests expose projected event amount as nullable Money and reuse existing money normalization semantics; account access behavior remains current-connection based. |
| specs | `affected` | Proposal, Delta Specs and this Design define the canonical domain and contract alignment; archive later syncs modified requirements into main specs. |
| docs | `affected` | Domain glossary, system/feature docs and any affected README/index references describe as-built canonical terminology after implementation without duplicating SDD artifacts. |
### Contract Gate Decision
| Gate | Status | Reason |
| --- | --- | --- |
| runtime-openapi | `required` | Backend response schema and frontend generated consumer types both change for Upcoming Events, so runtime OpenAPI must be compared and synchronized before completion. |
## Risks / Trade-offs
- **Breaking consumer contract.** Existing consumers using `amount` and
`currency` break. Mitigation: update generated types, fixtures and all
in-repo frontend consumers in the same change; no compatibility layer because
product is local and the old shape contradicts canonical Money.
- **Terminology-only churn hiding behavior changes.** Many docs/spec words
change without runtime changes. Mitigation: keep implementation edits scoped
to observable contract and targeted regressions; docs update only as-built
terms after verification.
- **Money uncertainty can be mistaken for zero.** Events without known monetary
terms must not show `0`. Mitigation: DTO and UI tests cover `null` projected
amount and existing «без суммы» rendering.
- **Currency instrument vs cash balance confusion persists in fixtures.**
Mitigation: add or preserve focused portfolio fixture/test proving
`CURRENCY` Portfolio Position remains separate from Cash Balance.
## Migration Plan
1. Update backend Upcoming Events DTO and mapping to expose nullable Money for
projected event amount while preserving event selection, sorting and
calculation timing.
2. Update backend tests for coupon/dividend/maturity Money, offer `null`, and
unchanged account access/error behavior where touched.
3. Regenerate or sync OpenAPI/frontend contract artifacts through the
established contract workflow and verify runtime OpenAPI.
4. Update frontend generated consumers, fixtures, event page and account
overview event block to render nullable Money directly.
5. Update frontend tests for known event amount and no-amount states.
6. Update docs glossary and affected feature/system documentation to as-built
terminology after implementation verification.
7. Run affected local gates plus the workspace change dispatcher before
archive.
Rollback is a normal code/docs/spec revert: no database migration, persisted
state migration, cache invalidation or dependency rollback is introduced.