架构设计
事件与 Outbox
了解项目里的 Outbox 事件机制,以及为什么它对文件、消息、AI 等链路很重要。
Outbox 是用来做什么的
Outbox 用来把“已经提交成功的业务事实”可靠地发布出去。
它适合:
- 缓存失效
- 索引更新
- 审计投影
- 异步通知
- 弱一致流程
它不适合代替同步 RPC 或在 payload 里塞复杂指令。
更直白一点说:主事务应该先把“业务事实”落稳,Outbox 再负责把这个事实可靠地传播出去。这样才能既保证主链路稳定,又给后续扩展留出空间。
当前仓库里的典型事件
AI_KNOWLEDGE_DOCUMENT_INDEXEDAI_KNOWLEDGE_DOCUMENT_DELETEDFILE_OBJECT_UPLOADEDFILE_OBJECT_DELETEDMESSAGE_NOTICE_CREATEDMESSAGE_NOTICE_RETRACTED
这些事件分别来自不同 owner 模块,不应该由外围模块“替别人补发”。
事件表关键字段
source_typeevent_typeevent_keypayload_jsondispatch_statusretry_countnext_retry_attrace_idrequest_id
其中最关键的几个字段可以这样理解:
event_key:消费幂等的第一道抓手dispatch_status:当前是否只是记录、正在投递、已投递或失败retry_count/next_retry_at:失败后的重试窗口trace_id/request_id:后续排查链路问题时非常有用
生产规则
- 事件必须围绕已提交事实
- 消费端必须幂等
- 失败后要允许重试
- 事件不能替代审计日志
建议再多记住两条:
- payload 只描述事实,不描述消费策略
- 事件归 owner 模块发布,不要跨模块伪造事实来源
标准 payload 应该长什么样
一个典型的 payload 通常包含:
schemaVersionoccurredAttenantIduserIdaggregateTypeaggregateIdattributes
attributes 里再放与本次事实直接相关的补充信息,例如文档标题、知识库 ID、chunk 数量、文件对象 ID 等。
典型链路
文件上传
写入 file_object
-> 记录 FILE_OBJECT_UPLOADED
-> 后续消费者可做 OCR、缩略图、病毒扫描、知识库解析这条链路的意义在于,上传接口不需要同步等待所有后处理完成。只要主对象写成功,后续消费者就可以按自己的节奏继续处理。
AI 知识库
保存知识库文档
-> 切 chunk
-> 记录 AI_KNOWLEDGE_DOCUMENT_INDEXED
-> 后续消费者可做向量化、索引刷新这个模式很适合知识库、搜索、消息、审计投影这类“主事实先写,衍生处理稍后完成”的场景。
消息通知
message-service 写入消息
-> 记录消息事件
-> WebSocket 实时投递
-> 投递失败时可重试或重放Relay 怎么接入运行
当前平台已经给 Outbox 预留了 relay 运行方式。典型模式是:
- owner 模块先写入
platform_event_outbox - relay 任务扫描
RECORDED或可重试事件 - 按配置投递到 logging、Redis Stream 或其他 dispatcher
在当前体系里,job-executor 可以通过内部任务接口触发 relay。
为什么这很重要
当系统继续演进时,很多看似“附带”的工作其实都不应该阻塞主事务。Outbox 能让主链路保持简单,同时让扩展点保持可靠。
常见错误
- 业务还没提交成功就先发事件
- 一个事件里塞入过多后续处理指令
- 消费端没有幂等保护
- 事件失败后没有可观察的重试状态
- 把 Outbox 当成审计日志的替代品