Initialize project scaffold and domain model

This commit is contained in:
a.tolmachev
2026-03-25 12:20:42 +03:00
commit fb302b2a2c
51 changed files with 6815 additions and 0 deletions
+355
View File
@@ -0,0 +1,355 @@
# Схема БД
## 1. Назначение документа
Этот документ фиксирует структуру хранения конфигураций, версий операций, загруженных артефактов и published runtime-view. Его цель - дать основу для SQL-миграций и для реализации `mcpaas-registry`.
В документе предполагается реляционная модель, ориентированная на `PostgreSQL`. Для MVP допускается адаптация под `SQLite`, но канонической считается схема, совместимая с `PostgreSQL`.
## 2. Общие принципы хранения
### 2.1. Версионирование обязательно
Конфигурация operation не должна храниться только в одной "живой" записи. Каждое существенное изменение должно приводить к появлению новой версии конфигурации.
### 2.2. Published и draft разделяются логически
- `draft` может меняться;
- `published` должна ссылаться на конкретную зафиксированную версию;
- runtime читает только опубликованные версии.
### 2.3. Артефакты и конфигурация не смешиваются
`.proto`, descriptor set, sample JSON и YAML import payload не должны храниться в той же структуре, что и runtime-ready configuration.
### 2.4. Секреты не хранятся внутри operation
В БД operation должны храниться только ссылки на auth profiles или secret references.
Для MVP рекомендуется отдельная таблица `auth_profiles`, где metadata и secret refs отделены от operation versions.
## 3. Основные таблицы
Минимальный набор таблиц:
- `operations`
- `operation_versions`
- `published_operations`
- `operation_samples`
- `descriptors`
- `auth_profiles`
- `yaml_import_jobs`
Опционально позже:
- `operation_test_runs`
- `audit_log`
## 4. Таблица `operations`
Хранит стабильную сущность операции, не зависящую от конкретной версии.
### Поля
- `id` `text primary key`
- `name` `text not null unique`
- `display_name` `text not null`
- `protocol` `text not null`
- `status` `text not null`
- `current_draft_version` `integer not null default 1`
- `latest_published_version` `integer null`
- `created_at` `timestamptz not null`
- `updated_at` `timestamptz not null`
- `published_at` `timestamptz null`
### Назначение
- быстрый список операций;
- стабильный идентификатор для UI и MCP;
- привязка к актуальному draft и опубликованной версии.
## 5. Таблица `operation_versions`
Хранит полную сериализованную конфигурацию конкретной версии operation.
### Поля
- `operation_id` `text not null`
- `version` `integer not null`
- `status` `text not null`
- `target_json` `jsonb not null`
- `input_schema_json` `jsonb not null`
- `output_schema_json` `jsonb not null`
- `input_mapping_json` `jsonb not null`
- `output_mapping_json` `jsonb not null`
- `execution_config_json` `jsonb not null`
- `tool_description_json` `jsonb not null`
- `samples_json` `jsonb null`
- `generated_draft_json` `jsonb null`
- `config_export_json` `jsonb null`
- `change_note` `text null`
- `created_at` `timestamptz not null`
- `created_by` `text null`
### Ключи
- primary key: `(operation_id, version)`
- foreign key: `operation_id -> operations(id)`
- рекомендованный composite foreign key для связанных таблиц: `(operation_id, version)`
### Почему так
Для MVP выгоднее хранить version snapshot целиком, а не дробить по десятку связанных таблиц. Это:
- упрощает versioning;
- упрощает откат;
- упрощает YAML export;
- хорошо сочетается с JSON-oriented доменной моделью.
## 6. Таблица `published_operations`
Хранит явную published-привязку, которую читает runtime.
### Поля
- `operation_id` `text primary key`
- `version` `integer not null`
- `published_at` `timestamptz not null`
- `published_by` `text null`
### Назначение
- быстрый доступ к published runtime-view;
- отсутствие двусмысленности, какая именно версия сейчас активна;
- простой invalidation для runtime cache.
### Рекомендуемая целостность
- `operation_id -> operations(id)`
- `(operation_id, version) -> operation_versions(operation_id, version)`
## 7. Таблица `operation_samples`
Хранит метаданные и ссылки на sample artifacts.
### Поля
- `id` `text primary key`
- `operation_id` `text not null`
- `version` `integer not null`
- `sample_kind` `text not null`
- `storage_ref` `text not null`
- `content_type` `text not null`
- `file_name` `text null`
- `created_at` `timestamptz not null`
### Варианты `sample_kind`
- `input_json`
- `output_json`
- `yaml_import_source`
### Назначение
- не класть большие sample payload в основные version records;
- иметь возможность переиспользовать или пересобирать draft mapping;
- отслеживать, из каких sample-данных строился черновик.
### Рекомендуемая целостность
- `operation_id -> operations(id)`
- `(operation_id, version) -> operation_versions(operation_id, version)`
## 8. Таблица `descriptors`
Хранит gRPC schema artifacts.
### Поля
- `id` `text primary key`
- `operation_id` `text null`
- `version` `integer null`
- `descriptor_kind` `text not null`
- `storage_ref` `text not null`
- `source_name` `text null`
- `package_index_json` `jsonb null`
- `created_at` `timestamptz not null`
### Варианты `descriptor_kind`
- `proto_upload`
- `descriptor_set`
- `reflection_snapshot`
### Назначение
- связывать gRPC operation с конкретной схемой;
- не хранить binary descriptor внутри основной operation version;
- иметь отдельную точку для discovery metadata.
### Рекомендуемая целостность
- если descriptor привязан к version, то `(operation_id, version) -> operation_versions(operation_id, version)`
## 9. Таблица `yaml_import_jobs`
Для MVP можно импортировать YAML синхронно, но таблицу под журнал импорта лучше предусмотреть сразу.
### Поля
- `id` `text primary key`
- `source_sample_id` `text null`
- `status` `text not null`
- `format_version` `text not null`
- `mode` `text not null`
- `result_operation_id` `text null`
- `result_version` `integer null`
- `error_text` `text null`
- `created_at` `timestamptz not null`
- `finished_at` `timestamptz null`
### Назначение
- аудит импортов;
- разбор ошибок валидации;
- поддержка будущего async import pipeline.
## 10. Таблица `auth_profiles`
Хранит переиспользуемые профили аутентификации для внешних вызовов.
### Поля
- `id` `text primary key`
- `name` `text not null unique`
- `kind` `text not null`
- `config_json` `jsonb not null`
- `created_at` `timestamptz not null`
- `updated_at` `timestamptz not null`
### Варианты `kind`
- `bearer`
- `basic`
- `api_key_header`
- `api_key_query`
### Правило
`config_json` должен содержать только `secret_ref`, а не открытые секреты.
## 11. Предлагаемая SQL-форма
```sql
create table operations (
id text primary key,
name text not null unique,
display_name text not null,
protocol text not null,
status text not null,
current_draft_version integer not null default 1,
latest_published_version integer null,
created_at timestamptz not null,
updated_at timestamptz not null,
published_at timestamptz null
);
create table operation_versions (
operation_id text not null references operations(id),
version integer not null,
status text not null,
target_json jsonb not null,
input_schema_json jsonb not null,
output_schema_json jsonb not null,
input_mapping_json jsonb not null,
output_mapping_json jsonb not null,
execution_config_json jsonb not null,
tool_description_json jsonb not null,
samples_json jsonb null,
generated_draft_json jsonb null,
config_export_json jsonb null,
change_note text null,
created_at timestamptz not null,
created_by text null,
primary key (operation_id, version)
);
create table published_operations (
operation_id text primary key references operations(id),
version integer not null,
published_at timestamptz not null,
published_by text null,
foreign key (operation_id, version)
references operation_versions(operation_id, version)
);
create table auth_profiles (
id text primary key,
name text not null unique,
kind text not null,
config_json jsonb not null,
created_at timestamptz not null,
updated_at timestamptz not null
);
```
## 12. Индексы
Минимально нужны:
- index on `operations(protocol)`
- index on `operations(status)`
- index on `operation_versions(operation_id, created_at desc)`
- index on `published_operations(version)`
- index on `operation_samples(operation_id, version)`
- index on `descriptors(operation_id, version)`
- index on `auth_profiles(kind)`
## 13. Versioning flow
### Создание операции
1. Создается запись в `operations`.
2. Создается версия `1` в `operation_versions`.
3. `current_draft_version = 1`.
### Изменение draft
1. Читается текущий draft.
2. Создается новая версия `n + 1`.
3. В `operations.current_draft_version` пишется новая версия.
4. Published версия не меняется.
### Публикация
1. Берется текущий draft version.
2. В `published_operations` upsert-ится ссылка на эту версию.
3. В `operations.latest_published_version` пишется та же версия.
4. Runtime cache получает сигнал на reload.
### Импорт YAML
1. YAML валидируется.
2. Определяется create или update сценарий.
3. Создается новая запись в `operation_versions`.
4. При необходимости создается запись в `yaml_import_jobs`.
## 14. Что не должно храниться в БД в таком виде
- секреты в открытом виде;
- runtime cache;
- скомпилированные adapter clients;
- невалидированные черновики, не приводимые к доменной модели.
## 15. Практический итог
Для MVP рекомендован такой подход:
- `operations` - стабильная идентичность;
- `operation_versions` - полные version snapshots;
- `published_operations` - текущая активная версия;
- `operation_samples` и `descriptors` - внешние артефакты;
- `auth_profiles` - переиспользуемая внешняя аутентификация;
- `yaml_import_jobs` - журнал импортов.
Эта схема хорошо ложится на `sqlx`, не требует избыточной нормализации и соответствует JSON-oriented модели домена.