12 KiB
Обзор системы
WealthLedger — локальное приложение для частного инвестора: оно связывает состав портфеля, операции, полученный доход и будущие выплаты по счетам T-Bank. Обзор объясняет систему сверху вниз. Детали сценариев и ответственность подсистем находятся в карте возможностей.
Поставленные возможности
- Аутентификация: access/refresh-сессии и защищённые маршруты.
- Подключение T-Bank: один зашифрованный token на User.
- Брокерские счета, Portfolio, Account Ledger, Пассивный доход и Future Cash Events.
- Frontend UI resilience: явные loading, error, empty и cached-snapshot состояния; initial и частичные loading-состояния показывают structural skeleton, а уже доступный контекст остаётся видимым при фоновом обновлении или действии пользователя.
- Все счета: общий портфель, журнал, доходы и будущие выплаты с сохранением принадлежности к счетам.
- Акции и облигации: поиск, рыночные данные, личная позиция и события конкретного инструмента.
Термины этих возможностей определяет глоссарий домена, а причины ключевых технических решений — ADR.
Контекст системы
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.
Исполняемые части
На уровне C4 containers здесь два приложения и локальная база. Каталоги
docs и specs не являются runtime-компонентами.
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 — локальный запуск.
Хранимые данные
Локальная база содержит идентичность пользователя и 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 владеет Session и одним T-Bank connection. Через подключение backend устанавливает доступ к Brokerage Accounts. Идентификатор счёта из URL сам по себе не подтверждает этот доступ.
Portfolio отвечает на вопрос «что есть сейчас», Account Ledger — «что исполнено», Realized Income — «какие выплаты уже получены», будущие события — «что ожидается». Эти представления нельзя подменять друг другом. Money сохраняет исходную валюту и точность; неизвестное значение отличается от нуля.
Режим «Все счета» агрегирует доступный состав, сохраняя происхождение строк; он не создаёт новый брокерский счёт. Обновление подключения меняет границу финансовых кэшей; перезапуск backend не восстанавливает прежние in-memory snapshots и cursor continuations из SQLite.
Поток auth/session
Вход создаёт локальную сессию; последующие запросы используют access token. После перезагрузки SPA восстанавливает сессию через refresh-cookie. Схема, конкурентная ротация, logout и ограничения собраны у одного owner: Authentication.
Поток инвестиционных данных
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 004 и ADR 005.