144 lines
9.8 KiB
Markdown
144 lines
9.8 KiB
Markdown
# ADR: канон External Provider Data Plane
|
||
|
||
Статус: **superseded**.
|
||
Дата: 2026-07-13.
|
||
Владелец решения: NODE.DC Platform.
|
||
|
||
> Заменено 2026-07-14 документом
|
||
> [`ADR_L2_OWNED_EXTERNAL_CONNECTORS.md`](ADR_L2_OWNED_EXTERNAL_CONNECTORS.md).
|
||
> Этот черновик неверно помещал provider adapter, normalisation и collection
|
||
> policy в `services/<provider>-gateway`. Новое правило: adapter принадлежит
|
||
> изолированному L2 workflow; Platform Data Plane остаётся provider-neutral.
|
||
|
||
## Контекст
|
||
|
||
Клиент может подключить к NODE.DC любой внешний продукт: телеметрию, ERP,
|
||
роботов, энергетику, BIM-систему или иной источник данных. Gelios Pro для
|
||
Gelios — первый конкретный поставщик для проверки канона, а не исключительная
|
||
архитектурная ветка. Нельзя превращать Engine workflow, Ontology Core или общую БД Platform
|
||
в место, куда попадают токены, raw payloads и частная логика каждого API.
|
||
|
||
Нужна повторяемая форма, в которой новый provider добавляется как отдельный
|
||
adapter, но получает общие правила scope, секретов, collection, хранения,
|
||
read-model, аудита и безопасной публикации в NDC.
|
||
|
||
## Решение
|
||
|
||
1. В `platform/packages/external-provider-contract` живёт общий versioned
|
||
контракт интеграции. Это не runtime и не база данных. Он задаёт форму
|
||
`provider`, `connection`, `capability catalog`, `credential reference`,
|
||
`access scope`, `field policy`, `collection profile`, `retention policy`,
|
||
`read model` и красный command-domain.
|
||
2. Каждый provider получает самостоятельный app-owned adapter в
|
||
`platform/services/<provider>-gateway`. Первый экземпляр —
|
||
`platform/services/gelios-gateway`. Adapter владеет intake contract, rate
|
||
budget, normalisation, collection policy, storage и своим internal
|
||
read/realtime API. Сам provider secret остаётся в Engine Credentials и
|
||
доступен только назначенному защищённому execution workflow.
|
||
3. Один provider service может обслуживать много клиентов. Каждая строка,
|
||
cursor, audit-event и read-model обязана иметь `tenant_id` и
|
||
`connection_id`; видимость provider account сама по себе не является
|
||
продуктовым scope. Для клиента с отдельными требованиями изоляции допустим
|
||
отдельный deployment/database profile без изменения контракта.
|
||
4. База принадлежит adapter-сервису, а не Ontology Core, Engine, Tasker или
|
||
общему Platform Postgres. Для пространственно-временного Gelios-кейса
|
||
базой служит PostgreSQL 16 + TimescaleDB + PostGIS (`gelios-postgres`).
|
||
Другой provider может выбрать иной storage engine только через явный ADR,
|
||
сохранив внешний контракт.
|
||
5. Ontology Core хранит только provider-neutral и provider-specific смыслы,
|
||
связи, правила и контракты. Он не хранит credentials, runtime telemetry,
|
||
customer raw payloads или renderer objects. Enforcement остаётся в
|
||
gateway/adapters. Engine Credentials хранит provider secret в границе
|
||
специально назначенного execution workflow; значение не сериализуется в
|
||
граф, ontology, логи, read-model или UI.
|
||
6. Engine L2 Collector использует credential reference и получает разрешённые
|
||
данные поставщика. Он передаёт в adapter только аутентифицированный нормализованный
|
||
intake payload. Остальные L2 workflow получают исключительно scoped
|
||
internal API/event contract и не читают gateway DB напрямую.
|
||
|
||
## Каноническая форма нового подключения
|
||
|
||
```text
|
||
Client / tenant
|
||
-> Engine Credential + protected Collector workflow
|
||
-> provider connection instance
|
||
-> provider adapter (safe intake policy + normalizer)
|
||
-> provider-owned storage and projections
|
||
-> internal read/realtime contract
|
||
-> L2 workflow / approved interface binding
|
||
-> renderer adapter
|
||
```
|
||
|
||
Каждый новый provider добавляет только свой adapter package, capability
|
||
catalog, schema mappings, scrubbed fixtures и domain ontology package. Он не
|
||
добавляет отдельную схему доступа к Engine/Studio и не создаёт прямой путь из
|
||
browser в provider API.
|
||
|
||
## Полнота данных без неконтролируемого объёма
|
||
|
||
«Предусмотреть все данные» означает каталогизировать каждую provider
|
||
capability и поле, а не опрашивать весь account на максимальной частоте.
|
||
Collection profile явно решает, какие safe-read capabilities, поля, scope и
|
||
частота включены в конкретной connection instance.
|
||
|
||
| Слой | Что хранится | Режим |
|
||
| --- | --- | --- |
|
||
| Capability catalog | documented endpoint/read-field/command capability и его риск | versioned source + ontology |
|
||
| Inventory/configuration | units, devices, groups, sensors, custom definitions | медленный reconcile |
|
||
| Current projection | последняя разрешённая позиция, состояние и display fields | idempotent upsert |
|
||
| Event history | нормализованные события и approved measurements | append-only, partitioned |
|
||
| Raw envelope | полный safe-read ответ с provenance и hash | restricted cold layer, retention-bound |
|
||
| Aggregates/features | rollups и признаки для аналитики/предиктива | derived, replaceable |
|
||
|
||
Raw envelope не выдаётся UI и не становится таблицей «всё в JSON навсегда».
|
||
Вначале он может быть compressed/partitioned storage с метаданными в БД; при
|
||
реальном объёме переносится в object storage, а PostgreSQL хранит immutable
|
||
index, hash, policy и ссылку. Retention, raw depth и downsampling утверждаются
|
||
после замера сообщений/сек, размера payload, требуемой истории, RPO/RTO и
|
||
стоимости. Так инженер может запросить ранее не показанное поле из
|
||
каталога/архива, не раздувая горячую read-модель.
|
||
|
||
## Realtime и интерфейс
|
||
|
||
Частота provider collection, обновления `current projection` и выдачи в UI —
|
||
три разные настройки. Например, map consumer может получать выбранную
|
||
read-модель раз в 3 секунды, но это не даёт ему права опрашивать Gelios раз в
|
||
3 секунды или создавать отдельный polling loop на каждого зрителя.
|
||
|
||
Gateway сначала обновляет одну current projection и публикует change event.
|
||
L2/Map binding затем может sampling/throttle этот поток по утверждённой
|
||
настройке интерфейса. Источник истины для live state — gateway storage, не
|
||
долгоживущий workflow и не Cesium session.
|
||
|
||
## Commands: моделируются, но не подключаются
|
||
|
||
Command templates, параметры, delivery states и audit входят в capability
|
||
catalog и ontology полностью. Read adapter не содержит send route и не
|
||
использует command capability. В будущем command execution создаётся только
|
||
как отдельный `provider-command-gateway`/red-domain deployment с явным
|
||
человеческим подтверждением, role/scope check, idempotency, audit и отдельным
|
||
security review. До этого команда не может быть отправлена из collector, L2,
|
||
Map или AI Workspace.
|
||
|
||
## Обязательные артефакты каждого adapter
|
||
|
||
- `provider manifest`: provider id, adapter version, auth modes, rate limits;
|
||
- capability and field catalog: read/write classification, source evidence,
|
||
pagination and error semantics;
|
||
- connection profile: tenant, secret reference, approved scope, field policy,
|
||
collection and retention profile;
|
||
- normalised contract and migrations; scrubbed fixtures and contract tests;
|
||
- health/metrics/audit without secrets or raw personal data;
|
||
- ontology package with stable subjects, relations and guardrails;
|
||
- internal read/realtime API contract; no browser/provider bypass.
|
||
|
||
## Не решено этим ADR
|
||
|
||
- конкретный deployment topology и HA/PITR target для каждого volume;
|
||
- выбор object storage после real-volume measurement;
|
||
- L2 stream execution contract и Module Studio binding implementation;
|
||
- параметры first Gelios collection profile и owner-approved connection scope.
|
||
|
||
Gelios-specific применение этого решения описано в
|
||
`docs/ADR_GELIOS_DATA_PLANE.md`.
|