Files
mnote/rust/design/core/01-domain-model-v0.md
T

12 KiB
Raw Blame History

01. Domain Model v0

更新时间:2026-04-11 适用范围:/mnt/Data1T/mnote-rust


1. 目标

本文件定义 mnote-rust核心领域模型

它要解决的问题不是“前端怎么渲染”,而是:

  • 系统里到底有哪些一等对象
  • 哪些对象是事实层真相,哪些只是派生视图
  • AI / CLI / Web / Desktop 应该围绕什么稳定对象工作
  • OnlyOffice、Mindmap、OCR、RAG 这类能力应挂在哪一层

本文件优先保证:

  • 长期稳定
  • 脱离 UI 依赖
  • 适合 Rust 内核实现
  • 适合 AI 通过结构化协议读写

2. 建模原则

2.1 事实与视图分离

以下对象属于事实层

  • Workspace
  • Page
  • Block
  • Asset
  • Reference
  • Task
  • CommandLog
  • Event
  • AgentSession

以下对象默认属于派生层 / 视图层

  • 目录(TOC
  • 反链聚合
  • 搜索结果列表
  • Mindmap 视图树
  • OnlyOffice 当前光标/当前页/当前选区
  • AI 面板当前消息列表 UI 状态

原则:

派生层可以重建;事实层必须稳定、可审计、可迁移。

2.2 人与 AI 共用同一套领域对象

不能做人类一套模型、AI 一套模型。

要求:

  • 人类编辑页面,本质是修改 Page / Block / Asset / Reference
  • AI 编辑页面,本质也是修改同样的对象
  • CLI 批处理、同步任务、导入器也修改同样的对象

2.3 第三方编辑器不是事实源

Block editor、OnlyOffice、mindmap editor 都不是系统真相。

它们只能是:

  • 某类对象的视图/交互适配器
  • 输入输出变换器
  • 外部文档能力宿主

2.4 结构化修改优先

优先通过:

  • 显式对象
  • 显式字段
  • 显式命令
  • 显式 ops

避免通过:

  • UI 状态猜测
  • 整文全文字符串替换
  • 第三方编辑器内部瞬时结构直接落库

3. 对象总览

Workspace
├─ Page
│  ├─ DocumentBody
│  │  └─ Block*
│  ├─ PageProperty*
│  ├─ PageLink*
│  └─ Snapshot*
├─ Asset*
├─ Task*
├─ AgentSession*
├─ CommandLog*
└─ Event*

Reference
├─ block -> block
├─ block -> page
├─ block -> asset-fragment
├─ page  -> asset
└─ task  -> page/block/asset

4. Workspace

Workspace 是系统的顶层容器。

建议字段:

  • workspace_id
  • slug
  • title
  • created_at
  • updated_at
  • owner_actor_id
  • default_locale
  • storage_policy
  • sync_policy
  • feature_flags
  • archived_at

职责:

  • 隔离页面、资产、任务、日志
  • 挂载存储策略与同步策略
  • 作为权限边界与导出边界

不负责:

  • 具体页面内容
  • UI 布局
  • 当前用户会话态

5. Page

Page 是笔记系统的主内容容器。

5.1 Page 的定位

它不是“某个编辑器文件”,而是:

  • 一条知识对象
  • 一个文档入口
  • 一个块树正文容器
  • 一个可被引用、索引、审计的实体

5.2 建议字段

  • page_id
  • workspace_id
  • parent_page_id(允许页面树)
  • title
  • slug
  • icon
  • cover_asset_id
  • body_root_block_id
  • page_typenote | doc | database_record | inbox | template | system
  • statusactive | archived | deleted
  • created_at
  • updated_at
  • last_edited_at
  • last_edited_by
  • current_revision

5.3 PageProperty

页面属性不应和前端表格视图绑死。

建议独立对象:

  • property_key
  • value_type
  • value
  • display_hint
  • source

可用于:

  • 标签
  • 时间
  • 状态
  • 优先级
  • 自定义结构化元数据

6. DocumentBody 与 Block

6.1 为什么以 Block 为正文主模型

因为从既有 mnote / mnote-next 的稳定人层语义看,真正稳定的是:

  • 单块编辑
  • 在某块后插入块
  • 删除块
  • 重排块
  • 缩进层级
  • 页面引用与块引用占位
  • 复杂块占位

这说明“块”比“整篇富文本 JSON”更接近系统真实操作单元。

6.2 Block 建议字段

  • block_id
  • workspace_id
  • page_id
  • parent_block_id
  • prev_block_id
  • next_block_id
  • sort_key
  • block_type
  • content
  • props
  • annotations
  • status
  • created_at
  • updated_at
  • created_by
  • updated_by
  • revision

6.3 BlockType 建议

最小集合建议:

  • paragraph
  • heading
  • bulleted_list_item
  • numbered_list_item
  • todo
  • quote
  • code_block
  • divider
  • callout
  • page_reference
  • block_reference
  • embed_asset
  • embed_view
  • embed_onlyoffice
  • embed_mindmap
  • embed_table
  • system_placeholder

原则:

  • 类型应描述语义,不描述某个前端组件名
  • embed_* 表示“外部能力挂件”,不是事实层自己变成那个系统

6.4 Block.content

建议:

  • 文本类块使用结构化 inline span 序列
  • 避免只存单纯 HTML
  • 避免直接依赖第三方编辑器私有 schema

建议形态:

{
  "spans": [
    { "type": "text", "text": "hello" },
    { "type": "page_ref", "page_id": "page_xxx", "title": "项目计划" },
    { "type": "text", "text": " world" }
  ]
}

6.5 Block.props

只放结构化、有限、可校验字段,例如:

  • level
  • checked
  • collapsed
  • indent
  • language
  • align
  • width
  • height
  • asset_id
  • view_id

避免把任意 UI 状态塞进 props。


7. Asset

Asset 是所有非正文主块树内容的统一挂载对象。

7.1 Asset 范围

包括:

  • 图片
  • PDF
  • docx
  • xlsx
  • pptx
  • 音频
  • 视频
  • 导出文件
  • OCR 中间产物
  • 结构提取结果

7.2 建议字段

  • asset_id
  • workspace_id
  • storage_key
  • original_name
  • mime_type
  • size_bytes
  • checksum
  • originupload / import / generated / synced
  • asset_kindimage / pdf / office / audio / video / binary / extracted_text
  • created_at
  • updated_at
  • created_by
  • status
  • latest_version_id

7.3 AssetVersion

附件应支持版本化。

建议:

  • asset_version_id
  • asset_id
  • version_no
  • storage_key
  • checksum
  • derived_from_version_id
  • created_at
  • created_by
  • change_reason

这样才能支撑:

  • OnlyOffice 编辑回写
  • OCR 重跑
  • 导入转换
  • AI 修改资产派生内容

8. Reference

Reference 是知识型系统的关键对象,不能只做正文里的临时 token。

8.1 Reference 范围

  • 块引用块
  • 页面引用
  • 资产片段引用
  • PDF 页码引用
  • Office 书签/段落锚点引用
  • URL 引用
  • 检索结果引用

8.2 建议字段

  • reference_id
  • workspace_id
  • source_object_type
  • source_object_id
  • target_object_type
  • target_object_id
  • anchor
  • label
  • snippet
  • confidence
  • created_at
  • created_by
  • ref_kind

8.3 Anchor

anchor 建议作为独立结构:

{
  "kind": "pdf_page",
  "page": 12,
  "bbox": null,
  "text_quote": "..."
}

或:

{
  "kind": "office_bookmark",
  "bookmark": "Heading_3",
  "text_quote": "..."
}

原则:

  • 锚点是系统对象,不是编辑器私有游标
  • 锚点失效时要能标记 stale,而不是静默消失

9. SelectionAnchor

SelectionAnchor 不是长期事实主对象,但在 Agent / 编辑器桥接中有价值。

它表示:

  • 当前选区
  • 当前光标
  • 当前活动页
  • 当前资产中的定位点

建议定位为:

  • 短生命周期上下文对象
  • 可存入 AgentSession / UI Session
  • 默认不直接作为主事实落库

因为:

  • 它变化太频繁
  • 容易和编辑器宿主耦合
  • 更适合作为命令输入上下文

10. Task

Task 既是用户任务,也是 AI 工作单元。

10.1 建议字段

  • task_id
  • workspace_id
  • title
  • description
  • task_type
  • status
  • priority
  • assignee_actor_id
  • source_page_id
  • source_block_id
  • input_payload
  • output_payload
  • created_at
  • updated_at
  • due_at
  • completed_at

10.2 为什么纳入领域模型

因为 AI 长期介入后,很多能力不是即时编辑,而是:

  • 导入任务
  • 提取任务
  • OCR 任务
  • 摘要任务
  • 索引重建任务
  • 同步任务

这些都应进入系统内核,而不是散在外部脚本里。


11. AgentSession

AgentSession 表示某次 AI 介入的上下文单元。

11.1 建议字段

  • agent_session_id
  • workspace_id
  • provider
  • model
  • initiator_actor_id
  • status
  • started_at
  • updated_at
  • ended_at
  • tool_policy
  • confirmation_policy
  • summary

11.2 不应存什么

不应把整份聊天 UI 状态直接当作领域模型核心。

建议分开:

  • 领域层只保留会话元信息、工具调用摘要、事件链路
  • 详细消息可作为附属日志或导出工件保存

12. CommandLog 与 Event

12.1 CommandLog

代表一次显式操作请求。

建议字段:

  • command_id
  • workspace_id
  • command_name
  • actor
  • source
  • target
  • payload_hash
  • reason
  • refs
  • idempotency_key
  • dry_run
  • status
  • requested_at
  • finished_at
  • error_code
  • error_message

12.2 Event

代表命令执行后产生的事实变化。

建议字段:

  • event_id
  • workspace_id
  • command_id
  • event_type
  • object_type
  • object_id
  • before_revision
  • after_revision
  • payload
  • created_at

原则:

  • 命令与事件分离
  • 先有意图,再有变化
  • 审计必须能回答“谁因为什么改了什么”

13. View / Derived Object

以下对象建议明确标注为派生对象,不直接当真相:

  • TOCView
  • BacklinkView
  • SearchHit
  • KnowledgeGraphView
  • MindmapProjection
  • OnlyOfficeProjection
  • PagePreview
  • DailyDigest

它们的共同特点:

  • 来自事实层投影
  • 可以缓存
  • 可以失效
  • 可以重建

14. Mindmap 的正确定位

从既有设计看,Mindmap 很适合走结构化 ops 协议。

但在 mnote-rust 中,建议将其定义为:

  • AssetViewProjection 的一种
  • Page / Block / Reference / Asset 投影生成或承载
  • 支持独立存储其节点视图结构
  • 但不让它成为全系统唯一知识主模型

建议:

  • 若是“页面内导图块”,则作为 embed_mindmap block 挂载
  • 若是“独立导图资产”,则作为 Asset(kind=mindmap)
  • AI 修改导图时,走 MindmapOp[],但最终仍写入系统命令日志

15. OnlyOffice 的正确定位

OnlyOffice 不应被建模为“Page 本体”,应被建模为:

  • Asset(kind=office)
  • AssetVersion
  • OnlyOfficeAdapterSession
  • SelectionAnchor(kind=office_*)
  • Reference(anchor=office_*)

也就是说:

  • Office 文档是系统资产
  • OnlyOffice 是该资产的一种编辑适配器
  • 编辑产生新版本与事件
  • AI 可以通过插件/适配器读当前选区、插入内容、替换内容
  • 但系统真相依然在你的领域模型与资产版本链里

16. v0 必须避免的错误

16.1 不要把前端 store 当领域模型

例如:

  • 当前页面 UI 选中态
  • 面板展开态
  • 聊天输入框草稿

这些不应进入核心领域。

16.2 不要把第三方编辑器 JSON 当系统法典

否则未来:

  • 替换编辑器会非常痛苦
  • AI 只能学编辑器私有结构
  • CLI 难以稳定操作

16.3 不要把全文字符串替换当主修改方式

必须保留结构化修改能力。

16.4 不要把向量库结果当事实对象

向量结果是检索派生物,不是知识真相。


17. 一句话结论

mnote-rust 的领域模型应以 Workspace / Page / Block / Asset / Reference / Task / AgentSession / CommandLog / Event 为核心; Mindmap、OnlyOffice、搜索结果、AI 面板都只是围绕这些核心对象形成的投影、适配器或任务系统,而不能反客为主。