# 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 reference scanner Возможна ситуация когда 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 ``` Periodic scanner (`OrphanReferenceScanner`, runs каждые 5 мин) находит такие orphan'ы и emit'ит metric: ``` nsi_orphan_references_total{source_dict="spacecraft",target_dict="satellite_type"} 1 ``` Alert `OrdinisOrphanReferences` срабатывает при > 0 за 10 мин. ## Cascade rules — roadmap (v2) Сегодня закрытие источника НЕ closes referencing automatically. Это дolжно явно делаться через 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 + metric `nsi_cascade_warnings_total`. - **cascade** — все referencing закрываются в той же транзакции. См. design doc `dict-relationships-v2` (внутренний artifact в `~/.gstack/projects/claude/`) для полного scope. ## Lineage discovery — roadmap (v2) Сегодня нет endpoint для "что depends on X". Admin должен вручную проходить schemas чтобы найти ссылки. В v2 планируется: - `GET /api/v1/dictionaries/{name}/dependents` — словари которые ссылаются. - `GET /api/v1/records/{dict}/{key}/dependents` — конкретные записи. См. design выше для подробностей. ## 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. ## Дальше - [Bundle cuod](bundle-cuod.md) — список текущих FK в проде - [События](events.md) — учитывай cascade closure при инвалидации