1. GORM 迁移概述
1.1 什么是数据库迁移
数据库迁移(Database Migration)是一种版本化、可逆的数据库结构变更管理方式。它允许团队像管理代码一样管理表结构、索引、约束的演进,确保开发、测试、生产环境的数据库始终处于一致的状态。
传统手动执行 SQL 脚本的方式存在三个核心痛点:
- 环境漂移:开发人员本地修改了表结构,但忘记同步给测试或生产环境
- 变更不可追溯:谁在什么时间改了哪个字段,没有版本记录
- 回滚困难:上线后发现结构变更导致故障,无法快速恢复到上一个版本
数据库迁移工具通过增量脚本(Up / Down)来解决上述问题。每次变更对应一个迁移文件,按时间戳或序号排序执行,并记录已应用的迁移版本。
| 对比维度 | 手动 SQL 脚本 | 数据库迁移工具 |
|---|---|---|
| 版本管理 | 无,依赖人工记忆 | 自动记录已执行版本 |
| 回滚能力 | 需手动编写逆向 SQL | 内置 Down 脚本支持 |
| 团队协作 | 容易冲突、遗漏 | 按顺序合并,可追溯 |
| 环境一致性 | 难以保证 | 全环境统一执行 |
1.2 GORM AutoMigrate 简介
GORM 提供了 AutoMigrate 方法,能够根据 Go 结构体定义自动创建或更新数据库表。它属于声明式迁移——你只需定义模型(Model),GORM 自动推导出对应的 DDL 语句。
核心用法:
// 自动迁移:根据 User 结构体创建或更新 users 表
db.AutoMigrate(&User{})
// 同时迁移多个模型
db.AutoMigrate(&User{}, &Order{}, &Product{})
AutoMigrate 支持的操作包括:
- 创建新表
- 添加新字段
- 修改字段类型(部分数据库支持)
- 添加索引和唯一约束
- 添加外键约束
⚠️ 注意:AutoMigrate 不会删除已存在的字段或表,也不会修改字段的 NULL / NOT NULL 属性(部分数据库)。对于破坏性变更,需要手动编写迁移脚本。
AutoMigrate 的工作流程可以用下图表示:
1.3 迁移与领域驱动设计的关系
在领域驱动设计(DDD)中,数据库表结构是领域模型的持久化映射。迁移工具承担了“模型契约”的版本管理角色——每次领域模型的调整,都对应一次数据库迁移。
这种关系体现在三个层面:
| DDD 概念 | 迁移中的对应 | 说明 |
|---|---|---|
| 聚合(Aggregate) | 主表 + 关联表 | 一个聚合对应一组迁移文件,保证数据一致性边界 |
| 值对象(Value Object) | 嵌入字段 / JSON 列 | 值对象通常不单独建表,迁移时需处理序列化 |
| 领域事件(Domain Event) | 事件表 / 日志表 | 事件溯源场景下,迁移需保证事件表结构稳定 |
| 限界上下文(Bounded Context) | 独立数据库 / Schema | 不同上下文使用独立的迁移序列,避免耦合 |
💡 最佳实践:将迁移文件按限界上下文组织目录,每个上下文拥有独立的迁移序列。这样当某个上下文重构时,不会影响其他上下文的数据库结构。
下图展示了 DDD 限界上下文与数据库迁移的映射关系:
通过将迁移与领域边界对齐,团队可以实现:
- 独立演进:每个上下文可以独立修改表结构,不影响其他上下文
- 契约清晰:迁移文件成为领域模型的“持久化契约”,代码与数据库一一对应
- 可追溯性:每次领域模型的调整,都能在迁移历史中找到对应记录