Files
mdm-ordinis/docs/integration/api/bitemporal.md
T
Zimin A.N. d5268b9395 docs: Diplodoc setup + полное руководство интеграции по API
Новая документация-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/).
2026-05-07 23:19:02 +03:00

186 lines
6.0 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.
# Bitemporal модель
Каждая запись Ordinis имеет двухмерное время:
- **Бизнес-время** (`validFrom` / `validTo`) — когда запись действует в реальности.
- **Транзакционное время** (Hibernate Envers `revinfo.timestamp`) — когда
изменение зафиксировано в БД.
Это две независимые оси: можно ввести запись задним числом
(business → past, txTime → now), либо запланировать на будущее
(business → future, txTime → now), либо retroactively изменить
прошлое (исправить ошибку — txTime → now, но business range уже past).
## Поля
```json
{
"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. Это:
1. Старая запись: `validTo = now()` (закрыта).
2. Новая запись: `validFrom = now()`, `validTo = 9999...`, `version + 1`.
Обе версии остаются в БД. История доступна через
`GET /records/{key}/history` (см. [Read endpoints](read-endpoints.md)).
```mermaid
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:
```bash
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
Можно ввести запись в будущее:
```json
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)
```bash
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
Закрытие нескольких записей одной транзакцией:
```bash
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:
```json
{
"closed": 3,
"skipped": [],
"errors": []
}
```
Каждая запись в собственной sub-transaction (Spring AOP per-record).
Если одна fail'нула с invalid_key — она в `errors`, остальные продолжают.
## Bulk export CSV
```bash
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=`**
```bash
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 в свою БД**
```sql
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()`. Можно указать:
```bash
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)](x-references.md) — связи между записями
- [Caching](caching.md) — учитываем bitemporal в инвалидации