11 KiB
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
最简单的定义,纯名字标识,无版本控制。
数据表:
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
数据表:
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
- bindings — 结构化查询条件,
- value_schema + initial_value(NOT NULL)
数据表:
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。
数据表:
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— 查询 ObjectGET /objects?type=<name>— 按类型列出 Objects
3.2 Event
不可变事件记录,id + type_hash + payload + created_at。
数据表:
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)
发射流程:
- 名字解析为 hash
- Schema 校验
- 写入
events+event_refs - 触发 reaction chain
API:
POST /events { type, payload }— 发射事件GET /events/:id— 查询事件GET /events?ref=<object_id>— 按 Object 查相关事件
3.3 Projection(缓存)
(def_hash, params_hash) → value 的 lazy 增量计算缓存。
数据表:
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)
);
计算流程:
- 查缓存,取
value+last_event_id - 对每个 source,用 bindings 查
id > last_event_id的增量事件 - 所有 source 的增量事件按
id ASC排序(integer 自增天然全序) - 逐条 dispatch 到对应 source 的 expression
- 更新缓存的
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)
数据表:
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? }— 创建 ReactionGET /reactions— 列出所有 ReactionsDELETE /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=<name> 按类型列出 Objects
POST /event-defs 注册/更新 Event 类型(upsert,版本链)
GET /event-defs 列出所有 Event 类型
POST /events 发射事件
GET /events/:id 查询事件
GET /events?ref=<id> 按 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— 单条事件 contextparams— 投影参数
- 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)