docs: reorganize system knowledge by domain
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# Приложения, хранилище и взаимодействия
|
||||
|
||||
Представление C4 Container раскрывает внутреннее устройство WealthLedger.
|
||||
Контейнер здесь означает приложение или хранилище, а не Docker-контейнер.
|
||||
Внешние системы совпадают с [System Context](context.md).
|
||||
|
||||
## Представление Container
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
U["Частный инвестор<br/>Человек"]
|
||||
subgraph W["WealthLedger · программная система"]
|
||||
F["React SPA<br/>TypeScript, Vite, Mantine, TanStack Query"]
|
||||
B["Backend API<br/>NestJS, Node.js"]
|
||||
D[("Локальная база<br/>Embedded SQLite")]
|
||||
F -->|"HTTP JSON /api/v1; Bearer и auth cookie"| B
|
||||
B -->|"Prisma + better-sqlite3; доступ к локальному файлу"| D
|
||||
end
|
||||
T["T-Bank Invest API<br/>Внешняя система"]
|
||||
M["MOEX ISS<br/>Внешняя система"]
|
||||
N["Реестр НКР<br/>Внешняя система"]
|
||||
C["T-Bank Brands CDN<br/>Внешняя система"]
|
||||
U -->|"Просматривает данные и управляет подключением"| F
|
||||
B -->|"gRPC over TLS; token пользователя"| T
|
||||
B -->|"HTTPS; публичные данные"| M
|
||||
B -->|"HTTPS; публичный HTML"| N
|
||||
F -->|"HTTPS; изображения без авторизации"| C
|
||||
```
|
||||
|
||||
SPA выполняется в браузере пользователя. Backend — отдельное локальное
|
||||
приложение; SQLite используется внутри его процесса через библиотеку и файл
|
||||
базы. Между API и SQLite нет сетевого database server. Граница системы на
|
||||
схеме показывает принадлежность частей, а не их размещение на одном узле.
|
||||
|
||||
## Ответственность частей
|
||||
|
||||
| Часть | Ответственность |
|
||||
| --- | --- |
|
||||
| React SPA | Экраны, навигация, формы, состояние входа и клиентский кэш ответов; форматирование денежных значений |
|
||||
| Backend API | Локальная идентичность, сессии, проверка доступа, подключение брокера, нормализация внешних данных, точные расчёты и серверные кэши |
|
||||
| SQLite | Долговременное хранение локальных пользователей, сессий и зашифрованных подключений |
|
||||
| Внешние системы | Данные брокера, публичные сведения об инструментах и изображения в пределах своей ответственности |
|
||||
|
||||
Frontend организован слоями `app → pages → features → entities → shared`:
|
||||
каркас связывает маршруты, страницы собирают сценарии, а общие средства
|
||||
обслуживают транспорт и представление. Backend остаётся модульным NestJS API,
|
||||
где локальная аутентификация отделена от подключения брокера и инвестиционных
|
||||
сценариев. Это внутренняя организация приложений, не набор микросервисов.
|
||||
|
||||
Финансовые HTTP-запросы браузера идут через backend. Пользователь вводит token
|
||||
T-Bank в форме и отправляет его для сохранения, но API не возвращает token
|
||||
в ответах; SPA не использует его для прямых брокерских запросов. Прямая
|
||||
загрузка публичного изображения из CDN не проходит через эту финансовую
|
||||
границу.
|
||||
|
||||
Типизированный frontend-клиент производится из OpenAPI backend во время
|
||||
разработки. В runtime SPA вызывает API, а не генератор или OpenAPI endpoint.
|
||||
Форма запросов и ошибок описана в [HTTP API](http-api.md); направление
|
||||
генерации принято в [ADR 002](../adr/002-nest-generated-openapi-contract.md).
|
||||
|
||||
## Хранимые данные и границы доступа
|
||||
|
||||
SQLite содержит пользователя с нормализованным email и hash пароля, его
|
||||
сессии с hash текущего refresh token, сроком действия и признаком отзыва,
|
||||
а также одно зашифрованное подключение T-Bank с временем подключения.
|
||||
Пароли хешируются bcrypt; token брокера шифруется AES-256-GCM. Plaintext
|
||||
credentials не являются сохраняемым представлением этих данных.
|
||||
|
||||
Сессии и подключение связаны с владельцем. Access token подтверждает
|
||||
локального пользователя, но `accountId` из URL сам по себе не даёт права
|
||||
читать брокерский счёт. Backend получает текущее подключение пользователя
|
||||
и его generation перед использованием финансового кэша. Проверка доступности
|
||||
счёта у брокера зависит от сценария: журнал выполняет её до чтения страницы
|
||||
кэша; свежий портфель одного счёта может быть возвращён без нового запроса
|
||||
списка счетов. При обновлении портфеля доступ проверяется у брокера.
|
||||
|
||||
Ротация сессии атомарно заменяет hash текущего refresh token: два
|
||||
конкурентных запроса не могут оба успешно выполнить одну ротацию.
|
||||
Вызов брокера и запись в локальную базу не образуют общую транзакцию:
|
||||
при сохранении подключения сначала проверяется token, затем записывается
|
||||
шифротекст. Подробности находятся в
|
||||
[аутентификации](../domains/authentication/overview.md) и
|
||||
[интеграции T-Bank](../integrations/tbank.md).
|
||||
|
||||
Брокерские счета, сводки, портфель, страницы журнала и будущие события
|
||||
не являются долговременной историей в SQLite. Они получаются для текущего
|
||||
подключения и могут временно кэшироваться. Перезапуск backend не
|
||||
восстанавливает прежние финансовые снимки и cursor continuations из базы;
|
||||
[модель свежести](cross-cutting/data-freshness.md) отделяет это от хранения
|
||||
учётных данных.
|
||||
|
||||
## Стратегия решения и её основания
|
||||
|
||||
Локальная база и Prisma дают типизированный доступ к локальному состоянию
|
||||
без отдельного сервера базы; исходные причины и пределы выбора сохранены
|
||||
в [ADR 001](../adr/001-prisma-sqlite-local-mvp.md). Централизованная
|
||||
[серверная граница T-Bank](../adr/003-tbank-server-side-grpc-boundary.md)
|
||||
сосредоточивает credentials, лимиты и повторы вне браузера, а
|
||||
[шифрование подключения](../adr/004-encrypted-per-user-tbank-token.md)
|
||||
задаёт способ хранения брокерского секрета.
|
||||
|
||||
[Политика сессий](../adr/006-auth-session-policy.md) разделяет короткоживущий
|
||||
access token и вращаемый refresh token. [Политика query-кэша](../adr/007-react-query-fetch-policy.md)
|
||||
сохраняет доступные данные при обновлении и не запускает refetch при возврате
|
||||
фокуса. [Ограниченные повторы T-Bank](../adr/005-tbank-upstream-resilience.md)
|
||||
и [структурированные журналы](../adr/008-pino-backend-logging.md) обслуживают
|
||||
внешнюю границу. ADR сохраняют контекст принятия решения; текущие детали
|
||||
уточняются в тематических описаниях.
|
||||
|
||||
## Качество и ограничения
|
||||
|
||||
Точность представления денег требует сохранять валюту и decimal-значение;
|
||||
неизвестная сумма не заменяется нулём. Свежесть наблюдения не равна
|
||||
актуальности рынка в реальном времени. Временные ошибки внешнего поставщика
|
||||
обрабатываются ограниченными повторами и локальными состояниями интерфейса,
|
||||
без обещания успешного ответа при недоступном upstream.
|
||||
|
||||
Авторизация доступа, недопущение token в публичный ответ и атомарность
|
||||
ротации сессии — отдельные свойства; наличие типизированного клиента
|
||||
не доказывает их. Известные расхождения регистрации и деталей валидации
|
||||
отмечены в [аутентификации](../domains/authentication/overview.md#регистрация-и-вход)
|
||||
и [HTTP API](http-api.md#error-contract).
|
||||
|
||||
Подтверждённая топология — локальная SPA и локальный API с файлом SQLite.
|
||||
Origin браузера, адрес API и cookie policy должны быть согласованы; условия
|
||||
и диагностика описаны в [локальном окружении](../development/local-workspace.md).
|
||||
Наличие локального запуска не задаёт production-топологию или гарантии SLA.
|
||||
|
||||
Кэши и лимитер T-Bank действуют на экземпляр backend. Ротация ключа шифрования
|
||||
подключений и согласование изменений подключения между распределёнными
|
||||
экземплярами не входят в реализованный механизм. Эти ограничения не
|
||||
позволяют переносить локальную схему на несколько экземпляров без отдельного
|
||||
решения о состоянии, credentials и лимитах.
|
||||
|
||||
Runtime-сценарии раскрываются у соответствующих владельцев:
|
||||
[восстановление сессии](../domains/authentication/overview.md#восстановление-сессии),
|
||||
[сохранение подключения](../integrations/tbank.md#проверка-и-сохранение-подключения)
|
||||
и [загрузка портфеля](../domains/portfolio/flows/portfolio-loading.md).
|
||||
Reference in New Issue
Block a user