# Cross-references (FK между справочниками) JSON Schema extension `x-references` объявляет, что значение поля должно ссылаться на active record в указанном словаре. ## Синтаксис ```json { "type": "object", "properties": { "satelliteTypeCode": { "type": "string", "x-references": "satellite_type.code" } } } ``` Format: `"."`. Когда POST/PUT приходит, Ordinis делает lookup в `satellite_type` где `data->>code == satelliteTypeCode`. Если не найден / не активен / вне scope — `422 Validation Error`. ## Текущие FK в bundle cuod | Source | Target | Поле | |---|---|---| | `spacecraft.satelliteTypeCode` | `satellite_type` | `code` | | `spacecraft.statusCode` | `spacecraft_status` | `code` | | `mission.spacecraftCodes` (array) | `spacecraft` | `businessKey` | | `instrument.spacecraftCodes` (array) | `spacecraft` | `businessKey` | | `instrument.supportedFormatCodes` (array) | `data_format` | `businessKey` | | `instrument.supportedLevelCodes` (array) | `processing_level` | `businessKey` | (Точное число и список могут меняться с bundle versions — см. [Bundle cuod](bundle-cuod.md).) ## Server-side validation При POST/PUT ReferenceValidator проверяет каждое x-references поле: | Симптом | Error code | Описание | |---|---|---| | Referenced dictionary не существует | `x_references_dict_not_found` | Опечатка в schema. | | Referenced record не найден / inactive | `x_references_target_not_found` | Bad value либо record закрыт. | | Value не string (для v1) | `x_references_non_string` | v1 поддерживает только string refs. | | Schema spec malformed | `x_references_malformed` | Bad schema. | Response: ```json { "errors": [ { "path": "/satelliteTypeCode", "code": "x_references_target_not_found", "message": "Referenced record not found in 'satellite_type' for code='UNKNOWN-TYPE'" } ] } ``` ## Scope hiding Если referenced dictionary — INTERNAL/RESTRICTED, а consumer имеет только PUBLIC scope, ReferenceValidator возвращает тот же error (`x_references_target_not_found`) что и на real not-found. Ordinis **не leak'ает** existence of out-of-scope dictionary через differential errors. ## Orphan references Возможна ситуация когда referenced record **закрывается**, а referencing record остаётся: ``` 1. spacecraft.RESURS-P-3 references satellite_type.EARTH-OPTICAL 2. Admin closes satellite_type.EARTH-OPTICAL (record.closed event emitted) 3. spacecraft.RESURS-P-3 теперь имеет dangling FK ``` Ordinis периодически детектирует такие orphan'ы серверной стороной — данные **не корректируются автоматически** (может быть intentional state до update referencing записей), но Ordinis team получает уведомление и может coordinate fix с data stewards. Consumer-side defensiveness: - При получении event `record.closed` для записи на которую ты ссылаешься — invalidate свой cache + либо fetch свежую версию referencing записи (если она тоже обновилась), либо handle dangling FK явно. ## Cascade rules — roadmap (v2) Сегодня закрытие источника НЕ closes referencing automatically. Это дoлжно явно делаться через bulk close либо individual updates. В v2 планируется schema extension `x-references-on-close`: ```json { "satelliteTypeCode": { "type": "string", "x-references": "satellite_type.code", "x-references-on-close": "block" } } ``` Опции: - **block** — закрытие источника невозможно пока есть active referencing (default). - **warn** — закрытие проходит, в response warning. - **cascade** — все referencing закрываются в той же транзакции, отдельные events emit'ятся per record. Конкретный timeline и финальная семантика будут опубликованы перед deployment v2 — следи за анонсами от Ordinis team. ## Lineage discovery — roadmap (v2) Сегодня нет endpoint для "что depends on X". В v2 планируется: - `GET /api/v1/dictionaries/{name}/dependents` — словари которые ссылаются. - `GET /api/v1/records/{dict}/{key}/dependents` — конкретные записи. Это позволит consumer'ам и UI узнавать связи без чтения всех schemas руками. Spec будет опубликован перед deployment v2. ## Best practices 1. **Используй businessKey как target field** для большинства FK. Это стабильный идентификатор. Альтернатива (`code`, `name`) работает но требует поддержки `x-unique` в target (иначе ambiguous matches). 2. **Не делай deeply nested refs** — v1 поддерживает только top-level fields. Nested objects с x-references — пропустят validation. 3. **Array refs работают** — если поле `type: array, items: {type: string, x-references: ...}`, каждый элемент валидируется. (Но в v1.2.1 это делается на каждый элемент отдельно — for large arrays медленно. Будет batch'нуто в v2.) 4. **Document FK в displayName** — `"description": "FK на satellite_type через code"` помогает при ревью schemas. ## Связанные schema extensions ### `x-localized` — мультиязычные поля ```json { "type": "object", "x-localized": true } ``` См. [read-endpoints.md](read-endpoints.md) `?lang=` параметр. ### `x-section` — bucketing полей в admin-UI form Hint для admin-UI: в какую визуальную секцию формы (`identity`, `references`, `description`, `dates`, `geometry`, `physical`, `extra`, либо custom string) сгруппировать поле. ```json { "type": "number", "x-section": "orbital_geometry" } ``` Если `x-section` не задан, admin-UI применяет эвристику: - `x-references` → `references` - `x-localized` → `description` - `format=date|date-time` → `dates` - key matches `/^(geo|geom|wkt|lon|lng|lat|altitude|coord)/` → `geometry` - `required` → `identity` - else → `extra` Predefined IDs получают i18n labels (Связи / Описание / Даты / Геометрия / Физические параметры / Идентификация / Дополнительно). Custom IDs humanize'ятся: `orbital_geometry` → `Orbital Geometry`. Только UI hint — backend не validate'ит и не reject'ит unknown values. ### `x-id-source` — авто-derive businessKey ```json { "x-id-source": "code" } ``` При CREATE businessKey берётся из `data.code` automatically (UI скрывает поле). См. ReferenceValidator. ### `x-unique` — уникальность поля в dict'е ```json { "type": "string", "x-unique": true } ``` Backend enforce'ит при CREATE/UPDATE. Используется для `code` полей в target dicts которые ссылаются по `x-references`. ## Дальше - [Bundle cuod](bundle-cuod.md) — список текущих FK в проде - [События](events.md) — учитывай cascade closure при инвалидации - [Schema workflow](schema-workflow.md) — как менять schema через approval flow