176 lines
11 KiB
Markdown
176 lines
11 KiB
Markdown
# consolidate-tbank-connection-resolution — Design
|
||
|
||
## Context
|
||
|
||
Backend T-Bank flows сейчас повторяют один knowledge cluster: чтение сохранённой
|
||
T-Bank connection по User, расшифровку token, запрос открытых accounts,
|
||
отсечение неподдерживаемых account types, проверку accountId для account-scoped
|
||
flows и перевод `INVALID_TBANK_TOKEN` в публичную ошибку API. Полная
|
||
account-scoped последовательность есть в Portfolio, Account Ledger и Future Cash
|
||
Events; список Brokerage Accounts использует тот же persisted connection и
|
||
supported-account rules без проверки конкретного accountId; T-Bank connection
|
||
flow отдельно валидирует candidate token и использует то же crypto/error
|
||
knowledge.
|
||
|
||
Поставленное поведение менять нельзя. Для account-scoped read flows отсутствие
|
||
connection остаётся `409 TBANK_NOT_CONNECTED`, invalid token остаётся
|
||
`400 INVALID_TBANK_TOKEN`, недоступный или неподдерживаемый account остаётся
|
||
`404`, а upstream availability errors сохраняют текущие `503`/throw-through
|
||
semantics. Account Ledger уже документирует, что connection и account access
|
||
проверяются до выдачи cached page; этот порядок должен сохраниться.
|
||
|
||
Ограничения backend: NestJS module/provider model, Node 22, public HTTP API
|
||
`/api/v1`, generated OpenAPI как frontend contract authority, canonical gate
|
||
`npm ci && npm run verify`.
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
|
||
- Сконцентрировать persisted T-Bank connection lookup, token decrypt,
|
||
supported-account policy, account access check и invalid-token translation в
|
||
одном backend module.
|
||
- Сохранить public API behavior, DTO shape, routes, status codes и Problem
|
||
Details codes.
|
||
- Сохранить service-specific responsibilities: Portfolio normalizes portfolio
|
||
data and snapshot fallback, Account Ledger maps operations and pagination,
|
||
Future Cash Events calculates projected events, Brokerage Accounts maps the
|
||
account list.
|
||
- Сделать новый module осмысленной test surface для shared access behavior,
|
||
сократив дублирование tests в feature services до orchestration и
|
||
feature-specific behavior.
|
||
|
||
**Non-Goals:**
|
||
|
||
- Новые T-Bank account types или изменение supported-account semantics.
|
||
- Frontend changes, OpenAPI snapshot changes, generated client updates или
|
||
runtime contract changes.
|
||
- Изменения Portfolio snapshot cache, Account Ledger page cache, Upcoming
|
||
Events dividend/instrument caches или metadata cache semantics.
|
||
- Durable storage, token versioning, account history или cache invalidation
|
||
after token replacement.
|
||
- Замена T-Bank gRPC client или добавление другой integration library.
|
||
|
||
## Decisions
|
||
|
||
**D1. Ввести один T-Bank access module с двумя public use cases.**
|
||
Module предоставляет list-oriented use case для supported Brokerage Accounts и
|
||
account-scoped use case для flows, которым нужен доступ к конкретному Brokerage
|
||
Account. Module владеет persisted connection lookup, token decrypt,
|
||
`getOpenAccounts`, supported-account filtering и inaccessible-account decision.
|
||
Feature services получают resolved token/account context или supported account
|
||
list и не повторяют access sequence.
|
||
|
||
*Alternative:* извлечь только маленький `supportedAccounts` constant и crypto
|
||
helper. Отклонено: access ordering и error translation остаются размазанными по
|
||
services, поэтому future account policy changes всё ещё могут разойтись.
|
||
|
||
**D2. Оборачивать account-scoped flow execution для централизованного
|
||
invalid-token translation.** Простой `resolveAccount(userId, accountId) ->
|
||
{ token, account }` не покрывает invalid-token errors, которые могут быть
|
||
выброшены более поздними T-Bank calls в том же service flow. Поэтому
|
||
account-scoped use case выполняет feature handler внутри translation boundary
|
||
access module. Missing connection, account lookup и handler failures используют
|
||
одно правило translation для `INVALID_TBANK_TOKEN`; unrelated exceptions
|
||
сохраняют существующее propagation.
|
||
|
||
*Alternative:* оставить `try/catch INVALID_TBANK_TOKEN` в каждом feature service
|
||
после shared resolver. Отклонено: это уменьшает duplication, но оставляет
|
||
behavior-critical string comparison в нескольких call sites.
|
||
|
||
**D3. Оставить T-Bank connection management вне account access, разделяя только
|
||
crypto/error primitives.** Сохранение или удаление T-Bank connection валидирует
|
||
candidate token и не разрешает persisted account. Этот flow не должен
|
||
принудительно проходить через account-scoped access use case. Shared module
|
||
может предоставить или переиспользовать crypto provider и invalid-token
|
||
translator, но connection management сохраняет текущую ответственность за token
|
||
validation, encryption и Portfolio snapshot invalidation.
|
||
|
||
**D4. Единая supported-account policy.** Supported upstream account types и их
|
||
normalized Investment Account Type/fallback display name живут в одной policy,
|
||
которой владеет T-Bank access module. Account list use case и account-scoped
|
||
validation оба опираются на эту policy, поэтому добавление или удаление
|
||
supported account type становится one-policy change.
|
||
|
||
**D5. Сохранить Account Ledger cache ordering.** Account Ledger должен
|
||
по-прежнему проверять current connection и account access до обращения к LRU
|
||
page cache. Implementation order: сначала access module, затем page cache,
|
||
затем upstream operations call при cache miss. Это сохраняет documented behavior
|
||
и 30-секундный token-replacement nuance, уже отражённый в as-built docs.
|
||
|
||
**D6. Tests переезжают к shared interface плюс тонкое service coverage.** Новые
|
||
tests покрывают observable branches access module: missing connection, invalid
|
||
token during account resolution, inaccessible/unsupported account, supported
|
||
account filtering и invalid token из wrapped account-scoped handler.
|
||
Существующие service tests корректируются так, чтобы проверять service-specific
|
||
behavior и факт использования shared access module без повторного тестирования
|
||
одной access matrix в каждом service.
|
||
|
||
## Delivery Map
|
||
|
||
| Area | Status | Architectural outcome or N/A reason |
|
||
| --- | --- | --- |
|
||
| frontend | `N/A` | Frontend behavior, generated client и OpenAPI snapshot не меняются. |
|
||
| backend | `affected` | T-Bank access module централизует connection lookup, token decrypt, supported-account policy, account access check и invalid-token translation; feature services сохраняют только feature-specific data retrieval, mapping и cache behavior. |
|
||
| specs | `affected` | Этот SDD change хранит Proposal, skipped Delta Specs, Design и Tasks для behavior-preserving refactor. |
|
||
| docs | `N/A` | As-built product behavior намеренно не меняется; docs обновляются только если implementation обнаружит ещё не задокументированный behavior nuance. |
|
||
|
||
### Contract Gate Decision
|
||
|
||
| Gate | Status | Reason |
|
||
| --- | --- | --- |
|
||
| runtime-openapi | `N/A` | Public API contract, DTO shape и generated frontend snapshot не меняются. |
|
||
|
||
### Solution Assessment
|
||
|
||
Формальная build-versus-buy assessment не triggered. Change не добавляет и не
|
||
заменяет auth, storage, external integration, observability, caching,
|
||
concurrency control или infrastructure. Он сохраняет существующий T-Bank gRPC
|
||
client и переносит project-owned access policy за более глубокий in-process
|
||
module.
|
||
|
||
## Risks / Trade-offs
|
||
|
||
- **Behavior drift during refactor.** Refactor затрагивает shared access
|
||
behavior нескольких endpoints. Mitigation: сохранить existing public status
|
||
codes/codes, добавить shared access tests для common matrix и оставить
|
||
feature-service tests для endpoint-specific behavior.
|
||
- **Over-wide callback interface.** Wrapping feature handlers централизует
|
||
invalid-token translation, но может создать впечатление, что access module
|
||
отвечает за feature logic. Mitigation: handler context ограничен resolved
|
||
token/account data; normalization, pagination, projections и caches остаются
|
||
в feature services.
|
||
- **Account Ledger cache ordering regression.** Перенос access code может
|
||
случайно начать отдавать cached pages до проверки current connection/account
|
||
access. Mitigation: Design требует access-before-cache ordering, а Tasks
|
||
должны включить focused regression test или semantic verification для этого
|
||
behavior.
|
||
- **T-Bank connection management partial duplication.** Connection settings flow
|
||
не может использовать account-scoped resolution, потому что валидирует
|
||
candidate token. Mitigation: разделить crypto/error primitives, но сохранить
|
||
connection management отдельно, не искажая account access interface.
|
||
|
||
## Migration Plan
|
||
|
||
Поставка выполняется одной backend-focused последовательностью без runtime
|
||
migration:
|
||
|
||
1. Добавить shared T-Bank access module и tests для common access matrix.
|
||
2. Перенести supported-account policy в shared module.
|
||
3. Refactor account-scoped flows так, чтобы они выполнялись через shared access
|
||
module, сохраняя feature-specific mapping, cache и fallback behavior каждого
|
||
flow.
|
||
4. Refactor Brokerage Accounts list на использование shared supported-account
|
||
policy/use case.
|
||
5. Оставить T-Bank connection management behavior неизменным, разделив только
|
||
безопасные crypto/error primitives, если это полезно.
|
||
6. Запустить backend canonical gate, semantic verification против этого Design
|
||
и Proposal, затем workspace change verification перед archive.
|
||
|
||
Rollback — обычный code revert: schema, data, contract или cache migration не
|
||
вводятся.
|
||
|
||
## Open Questions
|
||
|
||
<!-- none -->
|