model/目录只放纯结构体定义,禁止数据库操作;每个.go文件仅含struct声明、JSON标签和ORM字段映射标签,字段须导出且命名兼顾JSON与数据库兼容性,时间字段推荐time.Time类型,DTO应隔离存放于internal/shared/dto/。

model/目录里只放结构体定义,不写数据库操作
在Iris MVC中,“模型”不是DAO或Repository,它只是纯数据载体。你放进model/的每个.go文件,应该只包含struct声明、JSON标签、数据库字段映射(如gorm:或xorm:),**绝对不要出现db.Create()、gorm.DB字段、SQL语句或任何数据库连接逻辑**。
否则后续想换ORM(比如从GORM切到Ent),就得重写全部model/文件——因为结构体被污染成了“带持久化能力的实体”,违反了分层契约。
-
model/user.go示例:
package model
type User struct {
ID int64 `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"size:255"`
Email string `json:"email" gorm:"uniqueIndex"`
Balance int64 `json:"balance"`
}
- 所有字段必须导出(首字母大写),否则JSON序列化或ORM反射会失败
- 避免在结构体里嵌入
*gorm.DB、sql.Tx等运行时依赖 - 如果需要DTO(如登录请求体),应放在
internal/shared/dto/下,和model/物理隔离
结构体字段命名要兼顾JSON序列化与数据库兼容性
Iris本身不干预字段序列化,但HTTP响应和数据库写入都依赖结构体标签。常见踩坑是字段名大小写混乱导致前端收不到值,或ORM无法映射列。
- JSON字段名统一用
json:"xxx"小写下划线风格(如user_id),符合主流API规范 - 数据库字段映射用对应ORM的标签,例如GORM用
gorm:"column:user_id",XORM用xorm:"user_id" - 不要依赖“自动驼峰转下划线”——Iris不处理这个,GORM默认开启但易出错,建议显式指定
column - 时间字段推荐用
time.Time类型 +json:"created_at"+gorm:"autoCreateTime",避免int64手工转时间戳
别把model当成service或repository来用
知识库明确强调:model/里禁止出现db.Create()或gorm.DB字段。这不是风格建议,而是架构边界问题。
一旦你在User结构体里加一个DB *gorm.DB字段,它就不再是模型,而是“带数据库句柄的伪服务”。后果包括:
- 单元测试时无法干净地mock数据层
- 结构体被多个包import后,意外触发DB初始化或连接泄漏
- 迁移至其他存储(如Redis缓存层)时,必须先剥离所有DB字段才能复用结构体
正确做法是:把查询逻辑放到internal/user/repository/user_repository.go,让UserService调用它,User结构体只负责承载数据。
中型项目要按业务域拆model子包
当model/下文件超过10个,或某个结构体频繁被跨模块引用(比如User被post、order、notification同时依赖),说明它已具备独立演进能力。
此时应升级目录结构,在internal/下为每个主业务建独立包:
internal/
├── user/
│ └── model/user.go
├── post/
│ └── model/post.go
└── shared/
└── dto/login_request.go
这样做的实际好处是:
- 各模块
model可独立版本化(比如user/v2新增字段不影响post) - Go编译时能更精准地识别未使用代码,减少二进制体积
- 避免循环导入:以前
model/user.go和service/post_service.go互相import,现在通过shared/dto解耦
真正容易被忽略的是:模型的生命周期不该由HTTP请求驱动。它可能被CLI任务、消息队列消费者、定时Job反复使用——所以它的定义必须足够稳定、无副作用、不绑定任何运行时上下文。











