152 lines
8.3 KiB
Markdown
152 lines
8.3 KiB
Markdown
## Context
|
||
|
||
Account Ledger already preserves the signed upstream payment and the operation
|
||
identifiers, but the operations response has no instrument brand metadata. The
|
||
portfolio resolves the same metadata from the existing server-side T-Bank
|
||
boundary and uses the brand file name to render an image with an initial-based
|
||
fallback. The account overview and the full ledger currently render separate,
|
||
text-heavy operation layouts; the full ledger also keeps page data and JSX in
|
||
one component instead of the finance view-model boundary.
|
||
|
||
The public OpenAPI contract is the frontend authority. T-Bank rate limiting,
|
||
authentication and upstream failure handling remain owned by the backend.
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
|
||
- Supply nullable instrument brand metadata with each Ledger Entry without
|
||
altering its monetary or business meaning.
|
||
- Reuse one accessible, responsive presentation of a Ledger Entry in the
|
||
account overview and full ledger.
|
||
- Present the already-signed payment with a textual directional cue, a sign and
|
||
semantic positive/negative/neutral styling.
|
||
- Preserve local error, loading, empty, filter and cursor states.
|
||
|
||
**Non-Goals:**
|
||
|
||
- No new ledger categories, financial calculations, historical balance or
|
||
currency conversion.
|
||
- No changes to the T-Bank integration boundary, access rules, pagination or
|
||
query semantics.
|
||
- No logo enrichment for Future Cash Events or Portfolio Positions outside the
|
||
existing supplied metadata.
|
||
|
||
## Decisions
|
||
|
||
### Enrich Ledger Entries through a shared instrument-metadata resolver
|
||
|
||
The backend will extract the existing Portfolio instrument lookup and its
|
||
short-lived metadata caching into a reusable resolver. After obtaining a ledger
|
||
page, the service will resolve unique operation instrument identities through
|
||
that resolver and attach nullable `logoName` metadata to its entries. Lookup by
|
||
UID remains preferred; FIGI is the fallback. An unavailable or unresolvable
|
||
instrument remains a successful Ledger Entry with `logoName: null`; enrichment
|
||
must not hide a ledger page or change its money, category or identifiers.
|
||
|
||
This preserves the existing server-side T-Bank boundary and its rate limiting,
|
||
deduplicates repeated instruments in one page and avoids a separate visual-data
|
||
API. The shared resolver replaces Portfolio's private copy so the two consumers
|
||
have identical lookup and failure semantics.
|
||
|
||
### Make the additive contract explicit and nullable
|
||
|
||
`BrokerageOperationDto` gains the nullable logo file name as presentation
|
||
metadata. The generated backend OpenAPI and committed frontend snapshot remain
|
||
the only contract path. Existing consumers can ignore the additive field;
|
||
updated consumers must treat absence as normal and use the text fallback.
|
||
|
||
### Use a shared Ledger Entry presentation component
|
||
|
||
The frontend will expose a focused entity-level presentation component for a
|
||
Ledger Entry and use it both in the account overview and the full journal. It
|
||
will show the logo image when available, otherwise a deterministic initial
|
||
fallback; the visible operation and instrument text remains the primary label.
|
||
The existing image URL formatter is reused. Logo loading failure falls back in
|
||
place without moving the row.
|
||
|
||
The full ledger page is brought to the existing `container → ViewModel → view`
|
||
boundary while retaining its current query and interaction state. The ViewModel
|
||
prepares collection-level presentation data; the shared row owns only one
|
||
entry's display. This prevents duplication while keeping data fetching outside
|
||
presentation views.
|
||
|
||
### Convey money direction independently of colour
|
||
|
||
The row derives direction only from the sign already present in `payment.value`:
|
||
positive is a labelled `+` «Поступление», negative is a labelled `−` «Списание»,
|
||
and missing payment is the existing neutral «Без движения денег» state. Semantic
|
||
Mantine theme tokens supply positive, negative and neutral tones; the sign and
|
||
visible Russian label make the state understandable without colour. Amounts
|
||
remain formatted from their decimal string and are not converted to JavaScript
|
||
numbers for the direction decision.
|
||
|
||
The row uses a three-part hierarchy: instrument identity on the left, operation
|
||
and date/category as secondary metadata, and a right-aligned amount/direction
|
||
group. At narrow widths the metadata may wrap below the primary line while the
|
||
amount stays associated with its entry; the list never requires horizontal
|
||
scrolling.
|
||
|
||
## Delivery Map
|
||
|
||
| Area | Status | Architectural outcome or N/A reason |
|
||
| --- | --- | --- |
|
||
| frontend | `affected` | Generated contract update, reusable accessible Ledger Entry row, and aligned overview/full-ledger layouts through the finance VM/view boundary. |
|
||
| backend | `affected` | Shared server-side instrument metadata resolver and nullable logo metadata on each operations response. |
|
||
| specs | `affected` | Approved delta requirements become the versioned behavioural contract. |
|
||
| docs | `affected` | Account Ledger as-built documentation describes the delivered metadata and directional row semantics. |
|
||
|
||
### Contract Gate Decision
|
||
|
||
| Gate | Status | Reason |
|
||
| --- | --- | --- |
|
||
| runtime-openapi | `required` | Backend adds public response metadata and frontend consumes the generated contract. |
|
||
|
||
## Solution Assessment
|
||
|
||
The change extends an existing external T-Bank metadata lookup and introduces a
|
||
shared cache consumer, so it evaluates the available approaches.
|
||
|
||
| Option | Fit and maturity | Security and operating cost | Decision |
|
||
| --- | --- | --- | --- |
|
||
| Resolve every operation independently | Uses existing client but repeats calls for the same instrument and exposes journal latency to upstream volume. | Rate limiter prevents excess but adds queueing and avoidable upstream load. | Rejected. |
|
||
| Reuse Portfolio's private cache by duplicating it in the ledger | Works locally but creates divergent TTL, failure and fallback behaviour. | Low initial cost, growing maintenance and inconsistent cache pressure. | Rejected. |
|
||
| Extract a bounded, short-lived shared resolver around the existing client | Reuses mature authenticated boundary and gives both consumers one lookup policy. | No new credential or storage; cached public brand metadata is returned only after account access. Bounded memory and non-fatal failures limit operating risk. | Chosen. |
|
||
|
||
No third-party package or new external integration is justified: the T-Bank
|
||
client already supplies the required brand metadata and resilience controls.
|
||
|
||
## Risks / Trade-offs
|
||
|
||
- A page with many distinct instruments can still need several metadata lookups
|
||
on a cold cache. Deduplication, bounded cache lifetime and the existing
|
||
limiter trade initial completeness latency for upstream protection.
|
||
- Some past operations do not identify an instrument or have no brand asset.
|
||
The mandatory text fallback keeps these rows legible without inventing data.
|
||
- An additive nullable OpenAPI field requires atomic runtime-contract and
|
||
frontend-snapshot alignment. Until the compatible frontend is delivered,
|
||
old consumers safely ignore the field.
|
||
- Green/red alone can exclude users; sign and text are required alongside
|
||
semantic colours, and images remain supplementary to visible text.
|
||
|
||
## Migration Plan
|
||
|
||
1. Introduce the shared resolver and the nullable response metadata while
|
||
preserving the operations payload and failure behaviour.
|
||
2. Publish and sync the generated OpenAPI contract, then consume it in the
|
||
frontend presentation boundary.
|
||
3. Replace the duplicated overview and journal row rendering with the shared
|
||
responsive component and update the as-built feature documentation after
|
||
verification.
|
||
|
||
### Rollback Contract
|
||
|
||
| Field | Decision |
|
||
| --- | --- |
|
||
| Status | `required` |
|
||
| Reason | The change adds a public API response field consumed by the frontend. |
|
||
| Trigger | Runtime OpenAPI mismatch, unacceptable additional ledger latency, or a regression that hides ledger entries. |
|
||
| Actions | Revert the frontend to the prior row presentation and revert the backend response enrichment; retain the pre-change response shape when rollback is complete. |
|
||
| Data safety | No persistent data, migration or credential changes occur. In-memory metadata and page caches can expire or be discarded safely. |
|
||
| Verification | Re-run affected canonical gates, the runtime OpenAPI contract gate, and the ledger scenarios for signed, unsigned and missing-logo entries. |
|