164 lines
11 KiB
Markdown
164 lines
11 KiB
Markdown
## 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.
|