构建产品

数据库与迁移

选择 PostgreSQL 部署方式,并在不中断线上应用的前提下发布 Drizzle schema 变更。

已根据启动模板提交 e632762 验证。

完成本页后,你将知道各环境应连接哪个数据库、如何把 schema 修改变成可审查的 SQL,以及如何让数据库与应用独立发布。

模板明确选择 PostgreSQL 与 Drizzle,而不是同时维护多种数据库 adapter。本地可由 pnpm run setup 在 Docker 中运行 PostgreSQL;生产可选择任意兼容的托管或自建 PostgreSQL,并设置 DATABASE_URL。src/db/schema.ts 是 schema 的唯一事实来源,src/db/migrations/ 中已提交的 SQL 是可部署历史。只有 src/models/** 可以调用 db()。

本地流程

pnpm run setup 会创建独立的开发库和测试库。测试数据库名必须包含 test,否则安全保护会拒绝清空数据。

修改 schema 后运行:

pnpm db:generate
pnpm db:lint
pnpm db:migrate
pnpm test:db:setup
pnpm test:db

将生成的 SQL 和 meta/_journal.json 与代码一起提交,不要修改已部署的迁移。

生产流程

应用部署不会自动执行迁移。这是默认的安全策略:数据库和应用可以分别观察、重试和发布。

pnpm db:migrate:prod
pnpm db:check:prod
pnpm db:integrity -- --production

生产执行器非交互运行,使用 PostgreSQL advisory lock 并校验 migration checksum。先备份,并采用 expand/contract 变更,使旧代码和新代码都能在 schema 与应用分开发版期间工作。

数据约定

  • 数字 id 仅供内部使用,公开 API 使用 uuid。
  • 金额用最小货币单位的整数和币种保存。
  • 租户数据含 org_uuid,模型查询必须按组织限定。
  • 幂等键与交易号有数据库唯一约束。
  • 身份引用保持逻辑关系,因为账户删除会写入不可逆主体标识。任务到账本、Job 和输出文件等高价值关系已有外键,删除顺序仍由生命周期服务负责。
  • 预约还使用 PostgreSQL exclusion constraint 防止已确认或保留时段重叠。

上生产前要做的选择

  • 决定由 CI 发布任务还是运营人员执行迁移,不要让每个 Web 实例自动运行。
  • 制定与数据价值匹配的备份与恢复策略;迁移锁不能替代备份。
  • 关系变更前后运行 db:integrity。迁移 linter 会拒绝破坏性 drop、不安全 rename、无边界重写和阻塞式唯一索引,除非迁移包含可审核的发布说明。
  • 零停机发布优先使用 expand/contract。一步完成的破坏性重命名更简单,但会绑定 schema 与代码发布,并让回滚不安全。

确认备份可恢复、迁移已在 staging 副本验证、pnpm db:check:prod 通过且无待应用或漂移的历史,并保证新旧代码在发布期间可共存后,再推广应用代码。

修改生产数据契约前,请阅读模板内的 docs/database.md 与 DEPLOYMENT.md。

相关内容: 架构与错误契约说明数据层边界,认证与管理说明这些记录所关联的身份。

升级认证时先迁移数据库

当前版本使用 Better Auth 1.7.6。部署前先应用 SaaS 迁移 0037_neat_excalibur.sql,为 two_factor 增加 verified、failed_verification_count 与 locked_until。迁移只将已确认启用(users.two_factor_enabled=true)的记录标记为 verified;存在待确认 secret 并不代表已完成认证。迁移后在测试环境验证 MFA 注册、错误验证码锁定和 Admin 登录。

db:check:prod 只在所有预期迁移已应用且校验和匹配时通过;待应用或不匹配的历史会阻止发布。db:integrity 为只读检查,覆盖关联、无效值、可空性和孤立记录。向发布命令明确传入生产数据库变量。