# OGraph 对象模型与 API Protocol > OGraph v2.4+ 系统架构与 API 接口规范文档 ## 概述 OGraph v2.4+ 采用三层架构设计: ``` Definition Layer (定义层) ↓ Instance Layer (实例层) ↓ Reaction Layer (响应层) ``` ### 核心原则 - **事实不可变,解读可进化** — 原始事件永不修改,Projection 可以升级 - **名字是指针,hash 是锚点** — 名字在 API 入口解析,系统内部全用 content hash ### 命名规范 全部 **snake_case**,ASCII 小写,无空格无连字符。 | 实体 | 模式 | 示例 | |---|---|---| | Object Def | `{noun}` | `task`, `agent`, `project` | | Event Def | `{object}_{past_participle}` | `task_created`, `task_assigned`, `task_commented` | | Projection Def | `{描述性名字}` | `comment_count`, `current_assignee`, `active_tasks` | | Property | `{noun}` | `task`, `assignee`, `author`, `status` | | Params | `${property}_id` | `$task_id`, `$agent_id`, `$project_id` | **说明:** - Event 用过去分词 — 事件是已经发生的事实(`task_assigned` 而非 `assign_task`) - Params 带 `_id` 后缀 — 区分属性名本身(`assignee` 是 property,`$assignee_id` 是 param) - Object Def 用单数名词 — `task` 不是 `tasks` ## Definition Layer(定义层) ### 2.1 Object Def 最简单的定义,纯名字标识,无版本控制。 **数据表:** ```sql object_defs ( name TEXT PRIMARY KEY ) ``` **API:** - `POST /object-defs { name }` — 注册 Object 类型 - `GET /object-defs` — 列出所有 Object 类型 ### 2.2 Event Def(版本链) 定义事件的 schema 结构,支持版本演进。 **PropertyDef 类型:** - `ref` — 引用 Object,可带 `object_type` 实现多态 - `string` — 字符串 - `number` — 数值 - `boolean` — 布尔值 **版本链机制:** - Content hash 做 ID,确保内容唯一性 - `parent_hash` 串联形成版本链 - 名字是可变指针,指向当前版本的 hash **数据表:** ```sql event_def_versions ( hash TEXT PRIMARY KEY, name TEXT, parent_hash TEXT, schema TEXT, -- JSON created_at INTEGER ) event_def_names ( name TEXT PRIMARY KEY, current_hash TEXT -- → event_def_versions.hash ) ``` **API:** - `POST /event-defs { name, schema: { properties: {...} } }` — 注册/更新 Event 类型(upsert) - `GET /event-defs` — 列出所有 Event 类型 **特性:** - `ref` 类型的属性会自动提取到 `event_refs` 表 - 名字只在 API 入口解析,进入系统后全走 hash ### 2.3 Projection Def(版本链 + 多态 Sources) 定义如何将事件流聚合成状态视图。 **核心组件:** - **params** — 声明投影参数(通常是 ref 类型) - **sources** — 事件源数组,每个 source 绑定一种 event def hash: - **bindings** — 结构化查询条件,`$param` 引用 params,裸字符串是字面量 - **expression** — JSONata reducer,签名 `(state, event, params) → new_state` - **value_schema** + **initial_value**(NOT NULL) **数据表:** ```sql projection_def_versions ( hash TEXT PRIMARY KEY, name TEXT, parent_hash TEXT, params TEXT, -- JSON value_schema TEXT, -- JSON initial_value TEXT, -- JSON created_at INTEGER ) projection_def_sources ( projection_hash TEXT, event_def_hash TEXT, bindings TEXT, -- JSON expression TEXT -- JSONata ) projection_def_names ( name TEXT PRIMARY KEY, current_hash TEXT -- → projection_def_versions.hash ) ``` **API:** - `POST /projection-defs { name, sources: [{ event_def, bindings, expression }], params, value_schema, initial_value }` — 注册/更新 Projection(upsert) - `GET /projection-defs` — 列出所有 Projection 定义 ## Instance Layer(实例层) ### 3.1 Object 纯标识实体,`id + type + created_at`。 **数据表:** ```sql objects ( id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, -- → object_defs.name created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000) ); ``` **ID 策略:** - 内部 ID 使用 `INTEGER AUTOINCREMENT`,简单高效 - 外部系统标识(GitHub issue number 等)不放在 Object 表上,而是通过事件 payload 记录(如 `external_id_linked` 事件),由 Adapter 层自行管理映射 **API:** - `POST /objects { type }` — 创建 Object 实例 - `GET /objects/:id` — 查询 Object - `GET /objects?type=` — 按类型列出 Objects ### 3.2 Event 不可变事件记录,`id + type_hash + payload + created_at`。 **数据表:** ```sql events ( id INTEGER PRIMARY KEY AUTOINCREMENT, type_hash TEXT NOT NULL, -- → event_def_versions.hash payload TEXT NOT NULL, -- JSON created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000) ); event_refs ( event_id INTEGER NOT NULL, -- → events.id property TEXT NOT NULL, ref_id INTEGER NOT NULL, -- → objects.id PRIMARY KEY (event_id, property) ); CREATE INDEX idx_event_refs_obj ON event_refs(ref_id); ``` **ID 策略:** - Event ID 使用 `INTEGER AUTOINCREMENT`,严格递增,天然全序 - 排序只需 `ORDER BY id ASC`,不再需要 `ORDER BY created_at ASC, id ASC` - 为 Projection 增量计算提供精确边界(`WHERE id > last_event_id`) **发射流程:** 1. 名字解析为 hash 2. Schema 校验 3. 写入 `events` + `event_refs` 4. 触发 reaction chain **API:** - `POST /events { type, payload }` — 发射事件 - `GET /events/:id` — 查询事件 - `GET /events?ref=` — 按 Object 查相关事件 ### 3.3 Projection(缓存) `(def_hash, params_hash) → value` 的 lazy 增量计算缓存。 **数据表:** ```sql projections ( def_hash TEXT NOT NULL, params_hash TEXT NOT NULL, params TEXT NOT NULL, -- JSON: 原始参数 value TEXT NOT NULL, -- JSON: 计算结果 last_event_id INTEGER NOT NULL DEFAULT 0, -- 增量边界:上次处理到的 event id created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000), PRIMARY KEY (def_hash, params_hash) ); ``` **计算流程:** 1. 查缓存,取 `value` + `last_event_id` 2. 对每个 source,用 bindings 查 `id > last_event_id` 的增量事件 3. 所有 source 的增量事件按 `id ASC` 排序(integer 自增天然全序) 4. 逐条 dispatch 到对应 source 的 expression 5. 更新缓存的 `value` 和 `last_event_id` 无缓存时从 `initial_value` + 全量事件计算(`WHERE id > 0`)。 **增量精度:** 使用 `last_event_id`(integer)替代 `updated_at`(timestamp),精确到条,零遗漏零重复。 **API:** - `GET /projections/:name?param1=val1¶m2=val2` — 查询 Projection 值 ## Reaction Layer(响应层) ### 4.1 Reaction 订阅 projection 变化并执行响应动作。 **Action 类型:** - **webhook** — POST 到 `webhook_url`,payload 包含 `old_value`, `new_value`, `params`, `event` - **emit_event** — 发射新事件到 OGraph(事件衍生/扇出),支持 payload template(JSONata) **数据表:** ```sql reactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, projection_def_hash TEXT, params_hash TEXT, params TEXT, -- JSON: 原始参数 action TEXT, -- 'webhook' | 'emit_event' webhook_url TEXT, emit_event_type TEXT, emit_payload_template TEXT, -- JSONata created_at INTEGER ) ``` **触发链路:** ``` Event 发生 → 查 projection_def_sources 找到受影响的 projection → 查 reactions → 对有 reaction 的执行 bindings 匹配 → 重算 projection → diff(old, new) → 触发 action ``` **API:** - `POST /reactions { projection_def, params, action?, webhook_url?, emit_event_type?, emit_payload_template? }` — 创建 Reaction - `GET /reactions` — 列出所有 Reactions - `DELETE /reactions/:id` — 删除 Reaction ## 表结构总览 | 分类 | 表 | 可变性 | |---|---|---| | 定义(无版本) | `object_defs` | append-only | | 定义版本 | `event_def_versions`, `projection_def_versions` | immutable | | 定义 sources | `projection_def_sources` | immutable(随版本) | | 名字指针 | `event_def_names`, `projection_def_names` | mutable(UPDATE current_hash) | | 实例 | `objects`, `events` | append-only(ID 均为 INTEGER AUTOINCREMENT) | | 关联 | `event_refs` | append-only | | 缓存 | `projections` | mutable(缓存刷新) | | 响应 | `reactions` | CRUD | ## API 速查表 ``` POST /object-defs 注册 Object 类型 GET /object-defs 列出所有 Object 类型 POST /objects 创建 Object 实例 GET /objects/:id 查询 Object GET /objects?type= 按类型列出 Objects POST /event-defs 注册/更新 Event 类型(upsert,版本链) GET /event-defs 列出所有 Event 类型 POST /events 发射事件 GET /events/:id 查询事件 GET /events?ref= 按 Object 查相关事件 POST /projection-defs 注册/更新 Projection(upsert,版本链) GET /projection-defs 列出所有 Projection 定义 GET /projections/:name?params 查询 Projection 值 POST /reactions 创建 Reaction GET /reactions 列出所有 Reactions DELETE /reactions/:id 删除 Reaction GET /health 健康检查 GET /ui 管理界面 ``` ## Bindings 详解 **语法规则:** - `$param` — `$` 开头引用 params 值 - 裸字符串 — 字面量 **SQL 翻译:** 每个 binding 变成 `JOIN event_refs ON property = ? AND ref_id = ?` **特殊情况:** 空 bindings = 拉该类型全部事件(reducer 自行过滤) ## Expression 详解 **JSONata 表达式特性:** - **输入变量:** - `state` — 当前值 - `event` — 单条事件 context - `params` — 投影参数 - **Event context 包含:** - `id`, `type` (hash), `timestamp` - payload 展开的所有字段 - **执行方式:** 逐条 dispatch,不是批量 ## Reaction 触发链路详解 完整流程: ``` POST /events { type: "assigned", payload: {...} } ↓ 解析名字 → hash ↓ 校验 schema ↓ INSERT events + event_refs ↓ SELECT projection_hash FROM projection_def_sources WHERE event_def_hash = ? ↓ 对每个匹配的 projection: ↓ SELECT reactions WHERE projection_def_hash = ? ↓ 对每个 reaction: ↓ bindings 匹配(params → event_refs 查询) ↓ 重算 projection(lazy incremental reduce) ↓ diff(old_value, new_value) ↓ 如果有变化: ↓ webhook: POST webhook_url { old_value, new_value, params, event } 或 emit_event: POST /events { type: emit_event_type, payload: template(old, new, params, event) } ``` --- ## ID 策略总结 | 实体 | ID 类型 | 理由 | |---|---|---| | Object | INTEGER AUTOINCREMENT | 内部标识足够,外部引用通过事件 payload 记录 | | Event | INTEGER AUTOINCREMENT | 严格递增,做增量边界,天然全序 | | Reaction | INTEGER AUTOINCREMENT | 纯内部实体 | | Def versions | content hash (TEXT) | 内容寻址,版本链语义需要 | | Def names | name (TEXT) | 可变指针 | **原则:** 实例层全部 integer(性能 + 精度),定义层保持 content hash(语义需要)。 --- *本文档作为 OGraph v2.4+ 的技术规范,供系统集成和 API 调用参考。* *维护: 小墨 🖊️(KUMA Team)*