iris 不内置 mvc 分层与 crud 基类,需手动组织 models/controllers/service 三层结构并抽象接口;其定位是高性能路由引擎,mvc 和业务逻辑全由开发者用 go 特性实现。

直接说结论:Iris 本身不内置 MVC 分层约定,CRUD 封装必须靠你手动组织结构 + 接口抽象,不能依赖框架“自动”帮你分层。
很多人一上来就搜 Iris MVC,以为像 Spring Boot 那样开箱即用,结果发现 iris.New() 返回的只是个 *iris.Application,没有 Controller 基类、没有 Service 注入容器、更没有 Repository 模板。Iris 的定位是「高性能路由与中间件引擎」,MVC 是你用 Go 语言特性自己搭出来的——不是框架给的,是你写的。
为什么 Iris 没有内置 CRUD 基类
Iris 的设计哲学是「少约束、高自由度」。它不强制你用 struct 嵌套、不提供泛型基类(Go 1.18 之前没法写通用 BaseRepository[T])、也不绑定任何 ORM。这意味着:
-
gorm.Model或sqlc生成的 struct 可以直接当 model 用,但你要自己决定在哪一层调用db.Create() - 没有
BaseController,所以c.GetUserID()这种方法得你自己往context.Context里塞,或封装成中间件 - 如果你硬要用
interface{}写通用Create(obj interface{}),那类型安全、字段校验、错误映射全得自己补
实际可落地的三层封装结构(推荐)
参考你知识库里提到的 models/controllers/service 目录划分,这是目前社区最稳的实践。关键不是目录名,而是职责隔离:
-
models/:只放 struct 定义和 GORM 标签,不放任何方法。比如User结构体里不要写GetByEmail() -
service/:定义接口,如UserService interface { Create(*models.User) error; FindByID(int) (*models.User, error) }。实现放在service/mysql/下,用*gorm.DB做依赖 -
controllers/:只做三件事——解析参数(c.ShouldBindJSON())、调 service 方法、返回响应(c.JSON())。不碰 SQL、不处理事务边界
示例片段(非完整代码,仅示意结构):
// service/user_service.go
type UserService struct {
db *gorm.DB
}
func (s *UserService) Create(u *models.User) error {
return s.db.Create(u).Error
}
// controllers/user_controller.go
func Register(ctx iris.Context) {
var u models.User
if err := ctx.ShouldBindJSON(&u); err != nil {
ctx.StatusCode(400)
ctx.JSON(map[string]string{"error": err.Error()})
return
}
if err := userService.Create(&u); err != nil {
ctx.StatusCode(500)
ctx.JSON(map[string]string{"error": "create failed"})
return
}
ctx.StatusCode(201)
ctx.JSON(u)
}
容易踩的坑:泛型 CRUD 工具函数 vs 真实业务需求
有人会写一个 GenericRepo[T any],然后在 controller 里直接 repo.Save(&user)。短期看着爽,但很快会遇到问题:
- GORM 的
Preload()关联查询无法泛型推导,T不知道要预加载哪些字段 - 软删除(
DeletedAt)需要特殊处理,泛型函数不知道哪个字段是时间戳 - 不同 model 的创建逻辑差异大:User 要加密密码,Order 要生成单号,Product 要校验库存——强行塞进一个
Create()会导致 if-else 泛滥或类型断言满天飞 - 事务控制粒度变模糊:你是在 service 层开启事务,还是 controller 层?泛型 repo 通常没能力参与上层事务
真正省时间的不是“写一个通用 CRUD”,而是把重复代码抽成小工具:比如统一的分页封装 pageFilter()(你知识库里已有)、错误码转换函数、ID 解析中间件(从 JWT 提取 user_id 并注入 context)。
最后提醒一点:Iris 的 Context 不是 request-scoped 的全局变量,它生命周期只到 handler 返回。别试图在中间件里存 DB 事务对象然后在 controller 里取——GORM 的 Session 或 Transaction 必须显式传递,否则会 panic 报 invalid transaction。











