DRRK/ARCHITECTURE.md
Andrey Epifancev b4d49bdab8 docs: описать проект в CLAUDE.md и ARCHITECTURE.md
- CLAUDE.md: что это, стек и ограничения, архитектура, команды, тесты
- ARCHITECTURE.md: карта модулей, границы импортов, открытые вопросы
- .claude/code-paths: app/, alembic/, tests/ для хука require-plan
- .gitignore: .claude/worktrees/

Refs #1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:51:22 +04:00

37 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Архитектура
Streamlit-приложение поверх async SQLAlchemy. Слои: UI/джоба → сервисы → репозитории → БД; сервисы ходят во внешние API через интеграции и в разбор файлов через imports. Потоки данных по шагам — `README.md`, раздел «Потоки»; решения D… и вопросы Q… — `docs/project.md`.
## Модули
| Модуль | Ответственность | Зависит от |
|---|---|---|
| `main.py` | `load_dotenv` → `set_page_config` → `AuthService().run()` → `st.navigation` по страницам | `auth`, `core`, `ui` |
| `run_local.py` | локальный запуск: `LOCATION=local`, `alembic upgrade head`, `streamlit run` | — |
| `app/core` | `Settings` (pydantic-settings, префикс `OPENBAO__SETTINGS__` приоритетнее `.env`), логирование | — |
| `app/auth` | Keycloak через `raisa-streamlit-oauth`, mock-форма при `LOCATION=local` | — |
| `app/db` | async engine (`NullPool`), `SessionLocal`, `Base`, `run_db()`; типы колонок (BigInteger PK, JSON/JSONB, Numeric, аудит); ORM-модели | `core` |
| `app/integrations` | `gateway.py` — requests-клиент к Gravitee (ключ, таймауты, повторы, классификация ошибок, `RequestRecord`); `moex.py`, `cbr.py` — клиенты и разбор ответов в dataclass'ы | `core` |
| `app/imports` | `quik.py` — разбор выгрузки сделок QUIK (.xlsx), ошибки «строка N, колонка» | — |
| `app/repositories` | async-доступ к таблицам, upsert `ON CONFLICT` (`_upsert.py`) | `db`, `integrations` (только dataclass'ы результатов) |
| `app/services` | сценарии: `securities`, `market_data`, `fx_rates`, `quik_import`, `manual_operations`, `request_log`, `dates` | `db`, `repositories`, `integrations`, `imports` |
| `app/ui` | страницы «Бумаги», «Рыночные данные», «Загрузка сделок», «Ручные операции»; `common.py` | `services`, `imports`, `db` (`run_db`, перечисления моделей) |
| `app/jobs` | `update_market` — котировки + курс CNY за дату от пользователя `system`; коды выхода 0/1/2 | `core`, `db`, `services` |
| `alembic/` | async `env.py` со схемой `DATABASE_SCHEMA`, ревизии, сиды справочников | `db` |
Таблицы: `currency`, `portfolio`, `operation_type` (справочники с сидом), `security`, `security_nominal_history`, `market_data`, `market_data_manual`, `exchange_rate`, `operation`, `import_batch`, `external_request_log`. Уникальные ключи и смысл колонок — `README.md`, «БД и event loop».
## Границы
- `core`, `auth`, `imports` не импортируют другие пакеты `app`.
- `integrations` и `imports` не знают о БД; `integrations` — единственное место, где есть HTTP.
- `repositories` не вызывают внешние API: из `integrations` берут только dataclass'ы результатов разбора.
- `ui` не импортирует `repositories` и `integrations` напрямую — только через `services`; в БД ходит только через `run_db()`.
- Внешние API вызываются только из сервисов по действию пользователя или из джобы, не при рендере страницы.
- Каждый запрос к шлюзу пишется в `external_request_log` без тела ответа и заголовков (кроме `X-Gravitee-Transaction-Id`).
- Загрузка сделок QUIK пишет `import_batch` и операции одной транзакцией; план перед записью пересобирается по БД.
## Открытые вопросы
- Бизнес-вопросы Q2–Q28 — в `docs/project.md`; ключевые для следующей итерации — партии внутри выпуска (Q24), фондирование (Q8, Q19), хранение результатов расчёта (Q20), роль NOM и ADJ в результате (Q6, Q28).
- Где будут жить расчёты финрезультата и фондирования (новый пакет `app/calc/` с чистой логикой или сервисы) — решить на brainstorming первой расчётной итерации.
- Как запускать джобу по расписанию в Раисе — не решено.