arm-ka-docs/context/03-architecture.md
2026-10-02 23:02:44 +04:00

106 lines
9.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.

# 03. Архитектура (целевая, по решениям DECISION-1…5 и ARCH-* в DBML v5.13)
## Компоненты (= репозитории, см. `00-workspace-layout.md`)
```
┌──────────────┐ JWT (email) ┌──────────────────────────────┐
браузер ──────▶│ React SPA │ ───────────────▶│ arm-core (FastAPI) │
└──────────────┘ │ REST /api/v1, policy-слой, │
│ единственный писатель в БД │
└───────┬──────────┬───────────┘
│ │
PostgreSQL │ │ S3 (uploads/ results/ exports/ archive/)
│ │
NATS JetStream ◀─────────────────┘ outbox │
recognition.tabular.request │
recognition.image.request │
recognition.result ───────────────────▶ arm-core (inbox, дедуп по msg_id)
│
┌───────────────┴───────────────┐
┌────────▼────────┐ ┌─────────▼────────┐ ┌──────────────────┐
│ tabular-parser │ │ scan-gateway │ ◀─────▶│ ИС Сканирование │ (внешняя, горячие папки)
│ XLS/XLSX/XLSB │ │ PDF/PNG/JPG │ └──────────────────┘
│ stateless, без БД│ │ stateless, без БД│
└─────────────────┘ └──────────────────┘
```
- **arm-core** — монолитное ядро: справочники, файлы/документы, маппинг результатов распознавания, консолидация
(вычисления), согласование, ЕХ, аудит, права. Один Alembic, одна схема.
- **tabular-parser** — читает файл из S3 `uploads/`, находит на листах ОСВ (§1) и формы ББ/ОФР (§12) **по содержимому**,
пишет нормализованный JSON в S3 `results/`, публикует `recognition.result`. Контракт результата —
`docs/contracts/recognition-result.schema.json` (черновик; финализировать в срезе 2). Правила распознавания
заголовков/группировок — `rules/*.yaml`.
- **scan-gateway** — адаптер ИС Сканирование: переименовать файл `UUID_имя.pdf`, положить в «Входящие», опросить
«Исходящие/UUID/UUID.json», сохранить сырой ответ в S3 `raw/{job_id}.json` (в БД не регистрируется), нормализовать
в тот же контракт результата, опубликовать `recognition.result`. Таймаут 5 мин → `timeout`.
- **Frontend** — React + TS по шаблону команды; экраны по макету `dkb_24.09.26` (справочник ГК, консолидации,
первичные документы, проверка распознавания, сводная ОСВ, шахматка, корректировки, гудвилл, КФО, ЕХ, журнал, роли).
## Контракт обмена через NATS (DECISION-4)
| Поток | Subject | Кто пишет | Полезная нагрузка |
|---|---|---|---|
| запрос | `recognition.tabular.request` / `recognition.image.request` | arm-core (outbox из `recognition_job`) | `job_id`, `file_id`, `s3` (bucket/key исходника), `file_type`, `schema_version`, `reply_deadline` |
| результат | `recognition.result` | tabular-parser / scan-gateway | `job_id`, `status` (done/error), `error_code`, `service_name/version`, `result_s3` (bucket/key JSON), `result_schema_version`, `metrics`, `group_schema` (только tabular) |
Правила:
- `recognition_job` создаётся в статусе `queued` **в одной транзакции** со `stored_file`; фоновый публикатор переводит в
`published` (`request_msg_id` = `Nats-Msg-Id`). Повтор публикации — по `queued` старше N секунд.
- Результат принимается через durable consumer; `result_msg_id UNIQUE` = дедуп. После `max_deliver` → `dlq`;
replay = сброс в `queued` с `attempt + 1`.
- Результат одного файла содержит **N документов**; ядро на каждый создаёт `document` и маппит данные.
- Сервисы не знают про БД и про бизнес-смысл: они возвращают структуру файла; нормализация счетов, резолв
контрагентов, проверки — в ядре.
## Схема ключей S3
`{bucket_role}/{file_id}/{job_id}/{name}`; роли: `uploads` (исходники, срок — весь срок хранения), `results`
(нормализованные JSON; ретеншн — ARCH-010, открыт), `exports` (7 дней), `archive` (data lake). Все объекты регистрируются
в `s3_object` (sha256, size, expires_at). Сырой ответ OCR `raw/{job_id}.json` — по конвенции, без записи в БД.
## Аутентификация и авторизация
1. SPA получает JWT от РАИСА; каждый запрос несёт `Authorization: Bearer`.
2. arm-core валидирует подпись/срок, извлекает email, находит `app_user` (`is_active`); нет пользователя — 403.
3. Права: `effective_permissions = ∪ role_permission(roles(user))`. Проверка в зависимости FastAPI
`require(permission_code)`.
4. **Policy-слой** поверх прав (отдельный модуль, чистые функции): видимость по филиалу (`regional_office`),
по ЗО (`business_segment`), допустимость действия по статусу версии (`consolidation_status`) и по активной
блокировке (`consolidation_lock`). Право говорит «может», policy — «эту ли и сейчас ли».
5. Локальная разработка: заглушка-эмитент JWT с теми же claims (feature-flag `AUTH_DEV_ISSUER`).
## Слои arm-core (предложение; подстроить под шаблон команды)
Ниже — структура репозитория `arm-core`; `tabular-parser` и `scan-gateway` — отдельные репозитории по тому же шаблону
без `frontend/`.
```
backend/app/
api/ # роутеры FastAPI по контекстам: directories, files, documents, consolidation, kfo, ex, audit, users
domain/ # чистая предметная логика без I/O: osv parsing rules, period normalize, matching, elimination,
# checks, thresholds, status machine, policy
services/ # use-cases: оркестрация domain + repositories + outbox
repositories/ # SQLAlchemy, запросы-вычисления (сводная ОСВ, шахматка) — SQL + pandas там, где уместно
models/ # ORM 1:1 с DBML v5.13; имена таблиц/колонок — как в DBML
integrations/ # nats (outbox publisher, result consumer), s3, raisa
migrations/ # alembic
frontend/
openspec/
tests/ # фикстуры — из ../fixtures (ARM_KA_FIXTURES)
```
Принципы: `domain/` тестируется без БД на фикстурах из `../fixtures`; вычисляемые объекты консолидации возвращаются
как pydantic-модели и **не** имеют ORM-таблиц; все мутации идут через `services/` с записью `audit_event`.
## Статусы и машины состояний
Три независимые машины: `file_status` (техника файла), `document_status` (проверка аналитиком), `consolidation_status`
(маршрут КФО) + `recognition_status` (жизненный цикл задания). Переходы — `05-domain-rules.md` §9. Реализовать как
явные таблицы переходов в `domain/`, а не `if`-ы по роутерам.
## Что ожидаем от платформы (для запроса ресурсов)
PostgreSQL 15+ (одна БД, схема `public`), NATS JetStream (один stream `RECOGNITION`, 3 subject), S3-совместимое
хранилище (4 бакета по ролям или один бакет с префиксами + lifecycle), Gitea + CI (lint, pytest, миграции с нуля,
сборка образов), реестр образов. Секреты — через переменные окружения.