Drizzle ORM 数据库建模与 Repository 模式
学习目标
理解 LobeHub 如何用 Drizzle ORM 进行数据库建模,包括 schema 定义、relations 配置、repository 层封装和 Paradedb 全文搜索集成。
前置知识
本章无通用知识前置,直接聚焦 LobeHub 的工程实践。
项目实践
Schema 定义
LobeHub 在 packages/database/src/schemas/ 下定义了 30+ 数据表:
| 分类 | 表 | 说明 |
|---|---|---|
| Agent 核心 | agent、agentSkill、agentCronJob、agentOperations | Agent 定义、技能、定时任务、操作记录 |
| 对话 | message、session、topic | 消息、会话、话题 |
| 多 Agent | chatGroup | Agent 群组 |
| 记忆 | userMemories/* | 用户记忆(多层) |
| 任务 | task、asyncTask | Agent 任务 |
| 文档 | agentDocuments、documentHistory | Agent 文档树和历史 |
| 文件 | file | 文件管理 |
| 认证 | betterAuth、nextauth、oidc | 认证和 OAuth |
| 模型 | aiInfra、llmGenerationTracing | 模型基础设施和追踪 |
| 评估 | agentEvals、ragEvals | Agent 和 RAG 评估 |
| 其他 | user、apiKey、notification、rbac | 用户、API 密钥、通知、权限 |
Relations 配置
LobeHub 使用 Drizzle 的 relations 定义外键关系:
// 伪代码:Drizzle relationsexport const agentRelations = relations(agent, ({ many, one }) => ({ skills: many(agentSkill), cronJobs: many(agentCronJob), owner: one(user, { fields: [agent.ownerId], references: [user.id] }),}));Repository 模式
数据库操作通过 repository 层封装,不直接在业务代码中使用 Drizzle query:
// 伪代码:Repository 模式class AgentRepository { async findById(id: string): Promise<Agent | null> { ... } async create(data: CreateAgentInput): Promise<Agent> { ... } async update(id: string, data: UpdateAgentInput): Promise<Agent> { ... } async findByOwnerId(userId: string): Promise<Agent[]> { ... }}优势:
- 业务逻辑不感知数据库查询细节
- 便于单元测试(可以 mock repository)
- 数据库迁移只影响 repository 层
Paradedb 全文搜索
用户记忆模块使用 Paradedb 全文搜索替代普通的 LIKE 查询:
// 伪代码:Paradedb 搜索// 使用 paradedb.match(...) 而非硬编码 normalizer// 以获得更好的搜索相关性paradedb.match(memories.content, searchQuery)陷阱:Paradedb 是 PostgreSQL 的扩展,需要数据库中安装 paradedb 插件。本地开发环境需要配置。
Migration 管理
LobeHub 使用 drizzle-kit 管理迁移:
npm run db:generate # drizzle-kit generate + DBML 生成npm run db:migrate # 运行迁移脚本npm run db:studio # drizzle-kit studio(可视化数据库)问题与规避
| 问题 | 场景 | 规避策略 |
|---|---|---|
| Paradedb 未安装 | 本地开发环境 | Docker Compose 包含 Paradedb 容器 |
| 复杂查询性能差 | Drizzle 自动生成的 SQL 不如手写优化 | 复杂查询使用 .executeRaw() 手写 SQL |
| Migration 冲突 | 多人同时修改 schema | 使用 canary 分支,避免并行 migration |
| Relations N+1 | 循环中查询关联数据 | 使用 .with() 预加载关联 |
设计取舍
为什么用 Drizzle 而非 Prisma?
Drizzle 是 TypeScript-first 的 ORM,查询直接映射到 SQL,没有 Prisma 的中间层。
优势:
- 更轻量的运行时
- SQL 透明度高(可以直接看到生成的 SQL)
- 对 PostgreSQL 特性支持更好(如 Paradedb)
代价:
- 学习曲线(需要了解 SQL)
- 无内置 Prisma Studio(但 drizzle-kit studio 提供类似功能)
参考来源
- LobeHub database schemas —
packages/database/src/schemas/ - LobeHub database repositories —
packages/database/src/repositories/ - Drizzle ORM 文档