docs(specs): archive domain money contract change
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
## 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.
|
||||
Reference in New Issue
Block a user