这一组概念共同回答两个问题:数据长什么样,以及代码如何保存和读取它。RakuLLApp 要求 Model 拥有全部持久化状态,所以理解数据层是理解系统架构的关键。

数据库、SQL 与 SQLite

数据库用于长期保存和查询结构化数据。SQL 是操作关系型数据库的语言,例如查询文章或插入用户。SQLite 是一个把数据库保存在本地文件中的关系型数据库;Postgres 则通常作为独立数据库服务运行,更适合大型部署和并发访问。

Schema 与 DDL

Schema(数据库结构)定义有哪些表、字段、数据类型、约束和关系。DDL(Data Definition Language)是创建或修改这些结构的 SQL,例如:
CREATE TABLE articles (
  id INTEGER PRIMARY KEY,
  title TEXT NOT NULL
);
数据迁移(migration)是在保留已有数据的前提下,把旧 Schema 逐步升级到新 Schema。只修改建表文件并不会自动升级已有数据库。

实体、表与外键

  • **实体(entity)**是业务中的对象,例如文章、用户或收藏;
  • **表(table)**是关系型数据库中保存同类记录的结构;
  • **主键(primary key)**唯一标识一条记录;
  • **外键(foreign key)**表达记录之间的引用,并可由数据库检查完整性。
RakuLLApp 中每个 Model 服务有独立数据库。跨服务引用无法使用同库物理外键,因此采用 ID 形式的逻辑外键,由应用代码校验。

仓储接口是什么

仓储(Repository)把“业务代码需要的数据操作”与“具体如何读写数据库”隔开:
Controller / 领域逻辑 -> Repository 接口 -> PostgreSQL 实现(data_server)
上层只依赖 get_articlesave_sentence 等接口,不直接拼 SQL。更换存储实现时,上层调用方式可以保持不变。 仓储不是另一个数据库,也不等于 HTTP API;它是 Model 内部的数据访问边界。
这是通用架构模式。当前 RakuLLApp 只有 data_server 定义了明确的 Repository Protocol(含独立 PostgreSQL 库 rakull_collection 上的收藏域仓储,与 rakull_data 物理隔离);user_manager 的本地账号仍通过薄封装直接使用 SQLite(local_auth.db),而 collection_server 已不再持有数据库,只是转发到 data_server 的无状态薄网关。

对象存储是什么

关系型数据库适合结构化记录,大文件通常更适合对象存储。对象存储以 key 保存图片、音频或上传文件,上层数据库只保留对象 ID、路径、类型等元数据。 RakuLLApp 以 data_server 为界,为句子/情景音频和用户上传文件提供了统一的 ObjectStore / FileStore 接口:默认(OBJECT_STORE=local)把音频字节内联在 PostgreSQL rakull_data.media_objects.dataBYTEA 列),把上传文件写到本地 UPLOAD_DIR;生产环境(OBJECT_STORE=cos)把字节放到腾讯云 COS(Tencent Cloud Object Storage)桶,数据库行只保留 storage_key。两种后端上层调用方式保持不变;完整存储拓扑与 ER 图见存储架构,生产部署与 key 架构见对象存储(COS)

规范化与反归一化

规范化尽量只保存一份事实,通过引用组合数据,可减少重复和更新不一致。反归一化为了读取速度、离线使用或保留快照,有意复制部分数据。 例如收藏卡片保存加入时的 frontback 快照。这是有意的反归一化:原句以后改变,旧卡片仍能保持收藏时的内容。代价是数据重复,且不会自动同步。
“事实来源”指某项数据的权威所有者。复制的数据可以用于展示或缓存,但不能悄悄变成第二个权威来源。
查看项目实体和实际存储,请继续阅读领域模型与数据归属存储架构数据库结构