170 lines
12 KiB
Markdown
170 lines
12 KiB
Markdown
# Обзор системы
|
||
|
||
WealthLedger — локальное приложение для частного инвестора: оно связывает
|
||
состав портфеля, операции, полученный доход и будущие выплаты по счетам T-Bank.
|
||
Обзор объясняет систему сверху вниз. Детали сценариев и ответственность подсистем находятся
|
||
в [карте возможностей](../features/README.md).
|
||
|
||
## Поставленные возможности
|
||
|
||
- [Аутентификация](../features/authentication.md): access/refresh-сессии и
|
||
защищённые маршруты.
|
||
- [Подключение T-Bank](../features/tbank-connection.md): один зашифрованный
|
||
token на User.
|
||
- [Брокерские счета](../features/brokerage-accounts.md),
|
||
[Portfolio](../features/portfolio-view.md),
|
||
[Account Ledger](../features/brokerage-account-ledger.md),
|
||
[Пассивный доход](../features/brokerage-account-income-analytics.md) и
|
||
[Future Cash Events](../features/brokerage-account-upcoming-events.md).
|
||
- [Frontend UI resilience](../features/frontend-ui-resilience.md): явные
|
||
loading, error, empty и cached-snapshot состояния; initial и частичные
|
||
loading-состояния показывают structural skeleton, а уже доступный контекст
|
||
остаётся видимым при фоновом обновлении или действии пользователя.
|
||
- [Все счета](../features/all-accounts.md): общий портфель, журнал, доходы
|
||
и будущие выплаты с сохранением принадлежности к счетам.
|
||
- [Акции](../features/stock-detail.md) и [облигации](../features/bond-detail.md):
|
||
поиск, рыночные данные, личная позиция и события конкретного инструмента.
|
||
|
||
Термины этих возможностей определяет [глоссарий домена](domain-glossary.md), а
|
||
причины ключевых технических решений — [ADR](../adr/README.md).
|
||
|
||
## Контекст системы
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
U[Частный инвестор] -->|Смотрит инвестиции| W[WealthLedger]
|
||
W -->|Счета, позиции, операции и рынок| T[T-Bank Invest API]
|
||
W -->|Публичные данные облигаций| M[MOEX ISS]
|
||
W -->|Рейтинги выпусков| N[Реестр НКР]
|
||
W -->|Изображения логотипов| C[T-Bank Brands CDN]
|
||
```
|
||
|
||
T-Bank — источник личных брокерских данных и рынка. MOEX дополняет будущие
|
||
события и показатели облигаций, НКР — рейтинг конкретного выпуска. Публичный
|
||
рейтинг или календарь выпуска не подтверждает личное зачисление инвестору.
|
||
Отсутствие записи и недоступность источника имеют разный смысл; детали —
|
||
[Bond detail](../features/bond-detail.md).
|
||
|
||
## Исполняемые части
|
||
|
||
На уровне C4 containers здесь два приложения и локальная база. Каталоги
|
||
`docs` и `specs` не являются runtime-компонентами.
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph Browser[Браузер пользователя]
|
||
F[React SPA]
|
||
end
|
||
subgraph Local[Локальное окружение]
|
||
B[NestJS API]
|
||
D[(SQLite)]
|
||
B -->|Prisma: identity, sessions, connection| D
|
||
end
|
||
F -->|HTTP JSON, Bearer и auth cookie| B
|
||
B -->|gRPC over TLS, token пользователя| T[T-Bank Invest]
|
||
B -->|HTTPS, публичные данные| M[MOEX ISS]
|
||
B -->|HTTPS, публичный HTML| N[НКР]
|
||
F -->|HTTPS, изображения| C[Brands CDN]
|
||
```
|
||
|
||
- **Frontend** владеет представлением, навигацией и клиентским query cache.
|
||
Access token существует в памяти; refresh-cookie обслуживает browser.
|
||
- **Backend** владеет identity, проверкой доступа, credentials брокера,
|
||
нормализацией, денежными расчётами и upstream-кэшем.
|
||
- **SQLite** хранит локального User, sessions и зашифрованное подключение.
|
||
- **Интеграции backend** обращаются к T-Bank, MOEX и НКР, сохраняя различия
|
||
личных данных брокера и публичных данных выпуска. Браузер получает
|
||
изображения логотипов непосредственно из публичного CDN.
|
||
|
||
Frontend связывает пользовательские экраны навигацией. Backend объединяет
|
||
аутентификацию, подключение брокера и инвестиционные сценарии через публичный API.
|
||
Согласование адресов и origin — [локальный запуск](../development/local-workspace.md).
|
||
|
||
## Хранимые данные
|
||
|
||
Локальная база содержит идентичность пользователя и hash пароля, состояние
|
||
его сессий со сроком действия, признаком отзыва и hash текущего refresh token,
|
||
а также одно зашифрованное подключение T-Bank с временем подключения.
|
||
Сессии и подключение принадлежат конкретному пользователю; plaintext пароля
|
||
или broker token не является сохраняемым представлением credentials.
|
||
|
||
Изменения связанных локальных данных требуют сохранения их целостности.
|
||
Ротация сессии атомарно заменяет текущий refresh token: конкурентные запросы
|
||
не могут оба выиграть. Сетевой вызов брокера и изменение локальной базы не
|
||
образуют одну общую транзакцию. Финансовые наблюдения хранятся в ограниченном
|
||
кэше, а не как долговременная история брокерских данных.
|
||
|
||
## Локальные границы
|
||
|
||
- Финансовые API-запросы frontend проходят backend; browser не получает
|
||
T-Bank credential или gRPC client. Загрузка публичных логотипов — отдельный поток.
|
||
- Backend — NestJS API `/api/v1`, владеющий auth, SQLite-данными и T-Bank
|
||
gRPC boundary.
|
||
- OpenAPI backend — единственный контракт frontend; committed client
|
||
генерируется штатным workflow.
|
||
- Поставка остаётся локальной: deployment, Docker, CI/CD и облачные сервисы не
|
||
входят в текущие границы.
|
||
- Portfolio Snapshot, summary списка Brokerage Account, Future Cash Events и
|
||
Ledger pages существуют как read-only наблюдения текущего T-Bank connection;
|
||
система не хранит историю портфеля, будущих событий или брокерских счетов как
|
||
долговременные агрегаты. Summary списка сохраняет исходные валюты без FX-
|
||
агрегации и может быть локально недоступна для отдельного счета.
|
||
|
||
## Предметная модель
|
||
|
||
Локальный [User](domain-glossary.md#user) владеет
|
||
[Session](domain-glossary.md#session) и одним
|
||
[T-Bank connection](domain-glossary.md#t-bank-connection). Через подключение
|
||
backend устанавливает доступ к [Brokerage Accounts](domain-glossary.md#brokerage-account).
|
||
Идентификатор счёта из URL сам по себе не подтверждает этот доступ.
|
||
|
||
[Portfolio](domain-glossary.md#portfolio) отвечает на вопрос «что есть сейчас»,
|
||
[Account Ledger](domain-glossary.md#account-ledger) — «что исполнено»,
|
||
[Realized Income](domain-glossary.md#realized-income-analytics) — «какие выплаты
|
||
уже получены», будущие события — «что ожидается». Эти представления нельзя
|
||
подменять друг другом. [Money](domain-glossary.md#money) сохраняет исходную
|
||
валюту и точность; неизвестное значение отличается от нуля.
|
||
|
||
Режим [«Все счета»](../features/all-accounts.md) агрегирует доступный состав,
|
||
сохраняя происхождение строк; он не создаёт новый брокерский счёт. Обновление
|
||
подключения меняет границу финансовых кэшей; перезапуск backend не восстанавливает
|
||
прежние in-memory snapshots и cursor continuations из SQLite.
|
||
|
||
## Поток auth/session
|
||
|
||
Вход создаёт локальную сессию; последующие запросы используют access token.
|
||
После перезагрузки SPA восстанавливает сессию через refresh-cookie. Схема,
|
||
конкурентная ротация, logout и ограничения собраны у одного owner:
|
||
[Authentication](../features/authentication.md).
|
||
|
||
## Поток инвестиционных данных
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant U as User
|
||
participant F as Frontend
|
||
participant B as Backend
|
||
participant D as SQLite
|
||
participant T as T-Bank gRPC
|
||
U->>F: Открывает счета или Portfolio
|
||
F->>B: GET /api/v1/brokerage-accounts/* (Bearer)
|
||
B->>D: Читает подключение текущего User
|
||
B->>T: Проверяет доступ к счёту
|
||
B->>T: Запрос через server-side gRPC boundary
|
||
T-->>B: Upstream-данные
|
||
B-->>F: Нормализованные данные или Problem Details
|
||
F-->>U: Экран с данными или локальным состоянием ошибки
|
||
```
|
||
|
||
Схема показывает запрос, которому нужны upstream-данные. Повторные чтения могут
|
||
обслуживаться кэшем по правилам конкретной feature. Проверку connection/account
|
||
выполняет backend: запрос связывается с пользователем, его текущим подключением
|
||
и доступными через него счетами, включая выдачу данных из кэша.
|
||
Внешняя ошибка не становится нулевым балансом; допустимость stale snapshot и
|
||
локальное восстановление описывает соответствующий сценарий.
|
||
|
||
Причины server-side границы, шифрования и retry policy —
|
||
[ADR 003](../adr/003-tbank-server-side-grpc-boundary.md),
|
||
[ADR 004](../adr/004-encrypted-per-user-tbank-token.md) и
|
||
[ADR 005](../adr/005-tbank-upstream-resilience.md).
|