Drizzle Schema、Relations 与 Migrations 的关系
把 Drizzle 中 TypeScript Schema、数据库约束、Relations 查询声明与 Drizzle Kit Migration 放在同一张图里,区分“代码关系”“数据库约束”“历史演进”三个层次。
[!info] related notes
- 所属 MOC: nextjs-moc、database-moc
- 相关概念:
- 易混淆概念: orm-eager-vs-lazy-loading
- 相关资源:
Drizzle Schema、Relations 与 Migrations 的关系
为什么这三个概念必须分开
Drizzle 项目里常见三类“看起来都在描述关系”的东西:
TypeScript Schema
→ 当前代码认为表和列长什么样
Database Constraints
→ 数据库真正强制执行的合法状态
Relations
→ ORM 查询层如何方便地导航关联数据
Migration History
→ 数据库怎样从旧结构一步步演进到当前结构
它们相关,但不是同一个东西。
Schema:当前结构声明
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
authorId: integer('author_id')
.notNull()
.references(() => users.id),
})
Schema 让 TypeScript 查询获得类型信息,也可作为 Drizzle Kit 生成 migration 的输入。
但代码里的类型不会自动修复线上数据库;真正数据库结构必须通过 migration 等方式同步。
Foreign Key:数据库完整性
posts.author_id
→ references users.id
FK 是数据库层约束,负责阻止非法引用。
它解决:
这条引用在 durable state 中是否合法?
而不是:
ORM 查询时如何方便展开
post.author?
Relations:查询层导航
Drizzle Relations 用来声明实体之间的查询关系,使 relational query API 可以表达嵌套读取。
Foreign Key
→ database integrity
Drizzle Relation
→ query/navigation convenience
不能因为声明了 ORM relation,就认为数据库一定存在对应 FK;反过来也不能认为有 FK 就自动拥有所有 ORM 关系查询定义。
Migration:历史演进,而不是“当前 Schema 的复制品”
典型流程:
修改 schema.ts
↓
drizzle-kit generate
↓
生成 versioned SQL migration
↓
review SQL
↓
drizzle-kit migrate / deployment migration step
↓
Database schema changes
Migration 文件的价值是保存演进历史:
v1 → v2 → v3 → ... → current
因此 migration 不应被当作“随时根据当前 schema 重算即可”的临时产物。
为什么生产环境不要只依赖 push
开发环境中直接同步 schema 很方便,但生产环境更需要:
- 可审查 SQL
- 明确执行顺序
- 可追踪版本
- 数据回填步骤
- expand-and-contract 兼容窗口
所以稳定发布通常更偏向 versioned migration,而不是把当前代码结构直接强推到生产数据库。
Relation Query 与 JOIN
例如要读用户及其文章:
users
↓ relation
posts
ORM 可以提供嵌套 API,但底层仍可能生成 JOIN 或多查询策略。
真正需要关注:
- SQL 形状
- query count
- projection 宽度
- N+1
- index
因此:
漂亮的 nested object
≠ 自动高效 SQL
一张统一图
Domain requirement
↓
Table / Column / Constraint design
↓
Drizzle TypeScript Schema
├─→ typed query API
├─→ Relations declaration
└─→ Drizzle Kit diff
↓
Versioned Migration
↓
Real Database Schema
↓
Runtime Query / JOIN
最短记忆
Schema = current structure in code
Constraint = database-enforced invariant
Relation = ORM query navigation
Migration = structure history
Relation ≠ Foreign Key
Schema type ≠ DB constraint
Current schema ≠ migration history
这四层分开后,Drizzle 就不会变成“又一个神秘 ORM”,而只是把既有数据库概念映射到 TypeScript 工具链。