Закрыло за сутки: bundle 7→40 справочников, Альтум pull verified, Tempo+Loki+Grafana+SLO burn rate alerts (Google SRE multi-window), UI scope grouping + accents, INTERNAL словари visibility (rolesClaim), CNPG 1.24→1.25.4 + spec.probes, CI helm --atomic + auto-rollback, config-server startupProbe, 75→109 unit тестов. Готовы к prod (manifests + runbooks): multi-instance Postgres HA, external S3 backup, k6 benchmark setup. Block — DNS/cert prod, S3 provider creds, real ЦУОД data. Прогресс: 21/33 (v0.5) → 27/39 требований (v0.9). 1-2 недели до v1. v0.5 файл оставлен как историческая запись.
16 KiB
Состояние разработки НСИ (Ordinis ЦУОД ОДХ)
Версия: v0.5 (MVP+, anchor v1 — в процессе закрытия) Дата: 2026-05-05 Owner: zimin.an@nstart.space Окружения: staging (192.168.100.161 / cluster.265), prod (192.168.100.142 / cluster.142, hibernated)
1. Перечень требований
1.1. Реализовано (production-ready на staging)
| # | Требование | Статус | Где зафиксировано |
|---|---|---|---|
| 1 | Каталог НСИ ЦУОД ОДХ — единый источник истины | ✅ | bundle cuod v1.1.0 |
| 2 | Bitemporal модель (валидное-время + транзакционное-время) | ✅ | dictionary_records + valid_from/valid_to + Hibernate Envers |
| 3 | JSON Schema валидация записей (Draft-07) | ✅ | ordinis-validation (networknt) |
| 4 | i18n поля (RU + EN) с patternProperties | ✅ | x-localized: true extension |
| 5 | Уникальные поля (бизнес-ключ + произвольные) | ✅ | x-unique extension + native query enforcement |
| 6 | Авто-derive businessKey из data поля | ✅ | x-id-source extension |
| 7 | История версий записи + diff | ✅ | /records/{key}/history + UI drawer |
| 8 | Schema versioning + breaking-change detection | ✅ | diffSchemas + Save-block + auto major/minor/patch |
| 9 | Аудит-лог (кто, когда, что, IP, trace_id) | ✅ | audit_log + AuditLogger + UI /audit + CSV export |
| 10 | Kafka outbox (at-least-once гарантия) | ✅ | OutboxPoller + DLQ cap (1000 attempts) |
| 11 | Idempotency для мутирующих запросов | ✅ | Idempotency-Key header + sha-256 hash |
| 12 | Multi-scope доступ (PUBLIC / INTERNAL / RESTRICTED) | ✅ | DataScope + JWT roles + Kafka per-scope topics |
| 13 | Keycloak OIDC (issuer-uri auto-discovery) | ✅ | ordinis-auth + JwtDecoders.fromIssuerLocation |
| 14 | Visual schema builder + CRUD UI | ✅ | admin-ui (React 19 + TanStack + @nstart/ui) |
| 15 | SSO для admin-ui (PKCE Public client) | ✅ | react-oidc-context |
| 16 | Bundle import (idempotent seed) | ✅ | CuodBundleImporter startup runner |
| 17 | Swagger UI / OpenAPI 3 spec | ✅ | springdoc на /swagger-ui.html |
| 18 | 7 справочников + 51 запись на staging | ✅ | spacecraft (14), satellite_type (5), ground_station (3), mission (9), instrument (11), processing_level (8), data_format (8) |
| 19 | Friendly error responses (409/400/404 вместо 500) | ✅ | GlobalExceptionHandler |
| 20 | Метрики Prometheus + actuator/health | ✅ | ordinis_outbox_*, ordinis_kafka_* |
| 21 | Тесты | ✅ | 24 backend + 51 frontend = 75 unit-тестов, все зелёные |
1.2. В работе / required для v1 closure
| # | Требование | Статус | Блокер |
|---|---|---|---|
| 22 | Real consumer integration (Альтум — заказ съёмки) | 🟡 в процессе | Альтум собирает BFF (FastAPI), ждём первого pull от них |
| 23 | DNS A-record + Let's Encrypt cert на prod | 🔴 | DNS не проброшен на 192.168.100.142, prod hibernated |
| 24 | ORDINIS_AUTH_REQUIRED=true на prod |
🔴 | Зависит от готовности SSO admin-ui на prod + Альтум |
| 25 | Distributed tracing (Tempo) на prod | 🔴 | Не развёрнут observability stack |
| 26 | CNPG backup в S3 + DR rehearsal | 🔴 | S3-endpoint не настроен |
| 27 | SLO + alerts (Prometheus) | 🔴 | Не описаны метрики |
| 28 | 30+ справочников ЦУОД ОДХ (full coverage) | 🟡 | Сейчас 7 — anchor-набор. Нужны: антенны, частотные диапазоны, орбиты, операторы, потребители, контракты, зоны покрытия, климатические зоны и т.д. |
| 29 | Operator runbook | 🟡 | Частично: docs/integration, docs/tech. Нет процедуры unseal Vault, поднятия hibernated prod, рестарта Kafka |
| 30 | e2e API тесты (testcontainers) | 🔴 | Только unit |
| 31 | Component тесты frontend (RTL) | 🔴 | Только pure-logic |
| 32 | Bundle code-splitting (~660KB gzip) | 🔴 | echarts/lottie/@nstart/ui тянут единый chunk |
| 33 | Vault auto-unseal | 🔴 | Критично после reboot ноды — обнаружено 2026-05-05 |
1.3. v2 (governance / data quality / federation)
Не блокирует prod stand-up. Плановый roadmap (~2-3 квартала):
- Approval workflow (draft → review → publish), ownership trees, RBAC per-dictionary
- Lineage / dependency graph
- Geo first-class (карта ввода/превью, bbox/polygon query)
- Cross-dictionary FK с cascade rules
- Data quality rules engine (cross-field constraints, ML-anomaly detection)
- Federation MDM-кластеров (master/replica)
- Schema registry (Apicurio/Confluent) для Kafka
- Webhooks для не-Kafka consumer'ов
2. Нерешённые технические вопросы
2.1. Критические для prod
-
Vault auto-unseal. 2026-05-05 после reboot ноды staging Vault sealed → 3 Spring Boot pod'а залипли в Init на 3+ минут. Любой prod reboot = downtime + ручной шаг (3 unseal-ключа из 5). Решения:
- (a) Transit secret engine из master Vault (рекомендуется при наличии)
- (b) Yandex Cloud KMS / Selectel KMS auto-unseal
- (c) Скриптованный manual + runbook (минимум для v1, мигрировать позже)
-
Audience валидация JWT. Сейчас не enforced — принимаем токены от любого клиента в realm
nstart. Допускает scope-эскалацию через interactive client другого приложения. На prod нужно либо включить audience-check, либо доказать что Альтум client мапитaud=ordinisчерез Keycloak Audience Mapper. -
Outbox poll-interval 1 сек на prod. При 100+ событиях/сек → 86k+ запросов к Postgres/день. Возможно нужен batch tuning или reactive listener вместо poll.
-
Single-instance Postgres. На staging и prod — CNPG cluster с
instances: 1, реплика отсутствует. Failover нет. Для prod-нагрузки нуженinstances: 3с PgBouncer.
2.2. Архитектурные открытые вопросы
-
Realtime invalidation для consumer'ов. TTL 5 мин кэша достаточен для v1, но при критичных операциях (закрытие КА за день до запуска) нужен push. Kafka topic
nsi.dictionary.changedзапланирован на v2 — критичность для consumer'ов оценивается. -
Геоданные. В spacecraft.schema есть orbit-параметры, в ground_station — geometry (PostGIS). Но Альтум-задача (заказ съёмки) использует bbox/polygon — bitemporal-выборка по геометрии не реализована. Нужно подтверждение от ЦУОД что геопоиск нужен в v1 или v2.
-
Multi-tenancy. Сейчас
bundle: cuod— anchor customer. Второй клиент → второй bundle, своя тройка Kafka топиков. Но шаринг справочников между bundle (например spacecraft общий для ЦУОД и геопортала) не определён архитектурно. -
Object storage / большие справочники. Если справочник вырастет до 100k+ записей (например climate zones), JSONB в одной таблице может стать узким местом. Партиционирование
dictionary_recordsпоdictionary_id— потенциальное решение, нужна оценка.
2.3. Operational
-
Vault-agent-init backoff. ~3 минут при transient Vault unavailability — слишком долго. Нужен tuning либо переход на CSI Driver.
-
JDK 25 + Mockito inline несовместим. Перешли на subclass mock-maker. При апгрейде до Java 26 проверить.
-
CI path-filter. Был костыль, рассыпался при односторонних изменениях. Сейчас все docker jobs запускаются всегда (~30s extra на layer-cache). Можно вернуться к smart-filter когда GitLab variable interpolation в trigger jobs будет надёжной.
-
OTel exporter logs/metrics 404. Tempo на staging принимает только traces, logs/metrics endpoint отсутствует. Pod-логи замусориваются. Либо отключить logs/metrics export, либо поднять Loki/Mimir.
2.4. Compliance / правовые
-
Audit retention policy. Сейчас
audit_logпишется бессрочно. По регламенту ЦУОД нужно определить срок хранения (5 лет? 10 лет?) и автоматическую архивацию. -
Personal data handling. В audit_log пишется
user_id(Keycloak sub) + IP. ФЗ-152 — нужна оценка / DPA если в realm будут юзеры — не-сотрудники. -
Электронная подпись изменений. Если потребуется юридически значимая фиксация изменений справочников (КЭП оператора при close записи) — отдельный модуль, не реализован.
3. Наличие технической документации
3.1. Создана и актуальна
| Документ | Назначение | Расположение |
|---|---|---|
| API integration guide | Гайд для интеграторов (Альтум, геопортал): auth flow, endpoints, кэш-стратегии, bitemporal модель, businessKey'и | docs/tech/api-integration.md |
| Альтум integration ТЗ | Согласованный план интеграции «Заказ съёмки», схемы 4 справочников, BFF архитектура | docs/integration/altum-imaging-order.md |
| OpenAPI 3 spec (Swagger) | Машиночитаемая спецификация всех endpoints, можно генерить SDK | https://ordinis.k8s.265.nstart.cloud/swagger-ui.html + /v3/api-docs |
| Bundle manifest + schemas | JSON Schema каждого справочника + readable manifest | ordinis-cuod-bundle/src/main/resources/bundles/cuod/ |
| Архитектурные решения inline | JavaDoc + конструктивные комментарии в ключевых классах: ScopeContext, OutboxPoller, DictionaryRecordService, AuditLogger |
в коде |
| CI/Helm values | Параметризация per-env, CORS, ingress, image-tag override | ordinis-infra/charts/ordinis/{values,values-staging,values-prod}.yaml |
3.2. Отсутствует / требует написания
| Документ | Приоритет | Назначение |
|---|---|---|
| Operator runbook | 🔴 high | Procedure cards для типовых задач: unseal Vault, поднятие hibernated prod, рестарт Kafka StrimziPodSet, миграция БД, рестарт outbox-loop, разбор DLQ. Инцидент 2026-05-05 показал острую необходимость. |
| DR plan | 🔴 high | Backup → S3, RTO/RPO, процедура восстановления из CNPG snapshot |
| Архитектурный обзор (HLA) | 🟡 medium | Single-pager для нового тех-лида: модули, потоки данных, scope-фильтрация, bitemporal-логика — без необходимости читать код |
| User docs / справка оператора НСИ | 🟡 medium | Как создать справочник, как закрыть запись, как читать журнал аудита, как разобрать ошибку валидации схемы — для не-инженерных юзеров |
| Миграционный гайд | 🟢 low | Как мигрировать данные из существующих локальных справочников (Альтум, геопортал) в Ordinis bundle |
| Threat model + security review | 🟡 medium | OWASP-чеклист, audit роли, аудит trace-id, ограничения rate-limit на ingress |
| CHANGELOG.md | 🟢 low | Сейчас изменения видны только через git log. Для consumer'ов нужен structured changelog с breaking changes |
| Релизная политика | 🟢 low | semver для bundle vs schemaVersion vs Maven version. Сейчас не описана |
3.3. Inline-источники (читаются как docs)
git log --oneline ordinis-app/— история архитектурных решений в commit messages- JavaDoc / TSDoc на ключевых классах (~70% покрытия)
- TanStack Router file-based routing — структура UI самодокументируется
- Liquibase changelog'и в
ordinis-migrations/src/main/resources/db/changelog/changes/— история DB-схемы
Резюме
- Функционально v0.5 MVP покрывает 21 из 33 требований v1, недостающее — operational (observability, backup, runbook), а не feature-уровня.
- Архитектурный фундамент готов для масштабирования до 30+ справочников и 5+ consumer'ов.
- Главный риск — отсутствие auto-unseal Vault и DR-плана. Для prod stand-up — критично.
- Документация интеграторов готова (Swagger + 2 markdown). Документации для operations и end-users — недостаточно.
Прогноз до prod-ready (v1): ~2-3 недели при текущем темпе, при условии что Альтум-интеграция отвалидирована на staging до prod cutover.