Новая документация-as-code на платформе Diplodoc (Yandex open-source) с YFM (Yandex Flavored Markdown). Build → static HTML, deployable куда угодно. Что добавлено: - package.json + .yfm + toc.yaml — Diplodoc setup, навигация для sidebar - index.md — главная страница с tabs для разных audiences - README.md — инструкция для contributors Раздел integration/api/ — 11 страниц про интеграцию consumers: - index — обзор + глоссарий - auth — Keycloak service accounts, scope mapping - read-endpoints — GET /dictionaries, list/by-key, spatial filters - events — Kafka outbox топики, partition keys, idempotency, DLQ - webhooks — HMAC, SSRF guard, retry policy, test ping endpoint - bitemporal — validFrom/validTo + history, future-dated entries, bulk close/export - x-references — FK syntax, validation errors, scope hiding, orphan scanner, v2 cascade preview - caching — TTL / push / snapshot strategies, hybrid pattern - errors — full error code reference (validation/auth/conflict/server), retry strategy - best-practices — 33 numbered rules для consumers - bundle-cuod — все 40 справочников с FK графом и сценариями Обновлены: - ops/README.md — link на новую integration/api/, убран inline <br> - ops/slo.md — broken cross-repo links заменены на text references Build: cd docs && pnpm install && pnpm build → ./_dist (20 HTML files, 7.1M) pnpm serve # → http://localhost:8088 CI deploy job для GitLab Pages — в roadmap (см. docs/README.md). Существующий tech/api-integration.md оставлен в toc как legacy (содержимое мигрировано + расширено в integration/api/).
6.0 KiB
Bitemporal модель
Каждая запись Ordinis имеет двухмерное время:
- Бизнес-время (
validFrom/validTo) — когда запись действует в реальности. - Транзакционное время (Hibernate Envers
revinfo.timestamp) — когда изменение зафиксировано в БД.
Это две независимые оси: можно ввести запись задним числом (business → past, txTime → now), либо запланировать на будущее (business → future, txTime → now), либо retroactively изменить прошлое (исправить ошибку — txTime → now, но business range уже past).
Поля
{
"businessKey": "RESURS-P-3",
"validFrom": "2025-01-01T00:00:00+03:00",
"validTo": "9999-12-31T23:59:59+03:00",
"version": 2,
"data": { ... }
}
| Поле | Тип | Описание |
|---|---|---|
validFrom |
timestamp tz | Когда версия начинает действовать. |
validTo |
timestamp tz | До какого момента (или 9999-12-31 если бессрочно). |
version |
int | Optimistic lock + порядковый номер версии. Monotonic per businessKey. |
Update создаёт новую версию
Update — это не overwrite. Это:
- Старая запись:
validTo = now()(закрыта). - Новая запись:
validFrom = now(),validTo = 9999...,version + 1.
Обе версии остаются в БД. История доступна через
GET /records/{key}/history (см. Read endpoints).
gantt
dateFormat YYYY-MM-DD
title Версии записи RESURS-P-3
section v1
v1 (status=in-orbit) :2024-06-01, 2024-12-30
section v2
v2 (status=active) :2024-12-30, 2026-05-07
section v3
v3 (name updated) :2026-05-07, 9999-12-31
Запросы на момент
?at=2025-06-15 возвращает версию, которая была активна 15 июня 2025:
curl "https://ordinis.k8s.264.nstart.cloud/api/v1/spacecraft/records/RESURS-P-3?at=2025-06-15T00:00:00Z"
→ v2 (status=active), потому что 2024-12-30 ≤ 2025-06-15 < 2026-05-07.
Без ?at — default = now().
Future-dated entries
Можно ввести запись в будущее:
PUT /api/v1/dictionaries/spacecraft/records/MISSION-2027
{
"validFrom": "2027-01-01T00:00:00+03:00",
"data": { "name": "Запланированный КА", "status": "planned" }
}
Запись существует в БД, но GET ?at=now её не вернёт (потому что
now < validFrom). Появится автоматически 1 января 2027.
Закрытие записи (close)
POST /api/v1/dictionaries/spacecraft/records/RESURS-P-3/close
{
"at": "2026-05-07T12:00:00+03:00",
"reason": "Списан с орбиты"
}
Устанавливает validTo = at. После этого:
GET ?at=2026-05-07T11:00:00Z→ возвращает запись (ещё активна).GET ?at=2026-05-07T13:00:00Z→ 404 (уже закрыта).GET ?at=2026-05-07T11:30:00Z&include_closed=true→ для UI history.
Outbox emit'ит event action: closed.
Bulk close
Закрытие нескольких записей одной транзакцией:
POST /api/v1/dictionaries/spacecraft/records/_bulk-close
{
"businessKeys": ["RESURS-P-1", "RESURS-P-2", "RESURS-P-3"],
"at": "2026-05-07T00:00:00+03:00",
"reason": "Программа Ресурс-П завершена"
}
Response:
{
"closed": 3,
"skipped": [],
"errors": []
}
Каждая запись в собственной sub-transaction (Spring AOP per-record).
Если одна fail'нула с invalid_key — она в errors, остальные продолжают.
Bulk export CSV
GET /api/v1/dictionaries/spacecraft/records/_export?format=csv&lang=ru-RU
Stream CSV (RFC 4180) с активными на now записями. Используется UI bulk operations.
Параметры — те же что list endpoint (lang, at, фильтры).
Использование для consumer'ов
Snapshot at order time
Когда нужен «как было на момент заказа» — два варианта:
Вариант A: query с ?at=
GET /api/v1/spacecraft/records/RESURS-P-3?at=2025-06-15T10:00:00Z
Pro: всегда свежие данные если возможна retroactive correction. Con: depends on Ordinis availability + retention.
Вариант B: snapshot в свою БД
CREATE TABLE imaging_order (
id UUID PRIMARY KEY,
spacecraft_business_key VARCHAR(256),
reference_snapshot JSONB, -- копии записей, не FK
created_at TIMESTAMPTZ DEFAULT NOW()
);
Pro: read-only к Ordinis, не зависит от availability/retention. Con: stale если Ordinis retroactively исправил данные.
Альтум использует Вариант B (snapshot) для legal compliance — заказы attestation требуют не depending on external service.
Update с конкретным validFrom
Не всегда нужно validFrom = now(). Можно указать:
PUT /api/v1/dictionaries/spacecraft/records/RESURS-P-3
{
"validFrom": "2026-05-15T00:00:00+03:00",
"data": { "name": "Ресурс-П № 3 (новое имя с 15 мая)" }
}
Старая версия закрывается с validTo = 2026-05-15. Новая validFrom = 2026-05-15. До 15 мая ?at=now возвращает старую, после — новую.
Дальше
- Cross-references (FK) — связи между записями
- Caching — учитываем bitemporal в инвалидации