分层解耦取决于依赖流向和接口契约,而非目录命名;controller只依赖service接口,service只依赖repository接口,model仅为纯结构体;禁止跨层调用与框架包耦合。

分层不是靠目录名,而是靠依赖流向和接口契约
把代码扔进 model/、service/、controller/ 目录不等于分层解耦。Iris 本身不强制分层,真正的解耦体现在:controller 层只依赖 service 接口,不 import 具体实现;service 层只依赖 repository 接口,不碰数据库驱动或 ORM 实例;model 层是纯结构体,不带方法、不 import 任何框架包。
常见错误是 controller 直接调用 gorm.DB.Create() 或 redis.Client.Get(),这会让业务逻辑和数据访问强绑定,测试时无法 mock,部署时也无法切换存储后端。
- model 层只放
type User struct{...},不放func (u *User) Save() error - service 层定义
type UserService interface { GetUserByID(int) (*User, error) },不实现它 - 具体实现(如
userGormService)放在internal/serviceimpl/或pkg/下,且只在main.go中 new 并注入 - Iris 的
app.ConfigureContainer()是注册依赖的合理位置,但别把它当成 Spring 那样的全自动 DI
用 MVC 包做分层?小心自动映射反成枷锁
Iris 的 mvc.Application 确实能快速挂载 controller,但它默认按方法名(GetHome、PostLogin)自动绑定路由,容易掩盖职责边界。比如一个 UserController 里塞了注册、登录、密码重置、头像上传,表面是“一个 controller”,实际是多个业务场景混杂。
更危险的是,mvc 包会隐式注入 *iris.Context 和视图渲染逻辑,导致 controller 方法难以单元测试——你没法脱离 HTTP 上下文调用它。
- 如果要用
mvc,建议每个业务域(如 auth、profile、notification)单独建一个mvc.Application,绑定独立 Party 路由前缀 - 禁用自动方法名映射,显式调用
app.Handle(&AuthController{})后,在BeforeActivation中手动注册路由:router.Get("/login", c.Login) - controller 方法返回
mvc.Result时,避免直接写c.JSON();优先返回结构体或 error,由统一中间件处理序列化和状态码 - 别让 controller 调用
c.Session().SetString()—— 会把 session 细节泄露到业务层,应封装进AuthService.Login(ctx, user)内部
依赖注入怎么配才不翻车?别碰全局容器
Iris 的 ConfigureContainer 注册的是单例依赖,但企业级项目里,DB 连接池、HTTP 客户端、配置实例这些本该是共享的;而每个请求的 logger 实例、trace ID、用户上下文则必须按请求生命周期创建。混在一起注册,轻则内存泄漏,重则并发错乱。
典型翻车点:把 logrus.Entry 注入容器,结果所有请求共用同一个 Entry,字段被并发覆盖;或者把 context.Context 注入容器,导致超时和取消信号失效。
- 共享依赖(DB、Redis、HTTP client)在
main()开始就初始化,通过ConfigureContainer注册一次 - 请求级依赖(logger、tracer、auth.User)不要注册进容器,改用
ctx.Values()或自定义中间件注入到iris.Context中 - 测试时,用
app.WithDependency(...)替换特定依赖,而不是清空整个容器 —— Iris 不支持运行时重置容器状态 - 避免在
ConfigureContainer里做耗时操作(如连接数据库),失败会导致整个 app 启动失败,应提前校验
为什么 AnQiCMS 的五层架构在 Iris 里跑得稳?
AnQiCMS 的表现层、控制器层、服务层、模型层、数据层划分,并非靠 Iris 框架能力支撑,而是靠工程约束:每个层只 import 下层接口,禁止反向引用。它的稳定来自明确的边界线,而非框架魔法。
比如它的插件系统,每个 Plugin 控制器都实现 PluginController 接口,由主程序统一注册到指定 Party 下;插件内部完全不 import 主程序的 app 实例,只通过接口接收配置和依赖。这种“接口即契约”的做法,才是可维护性的核心。
真正容易被忽略的复杂点是:当服务层需要跨多个数据源(MySQL + Elasticsearch + Redis 缓存)协同完成一个业务操作时,事务一致性不能靠框架保证,必须靠 Saga 模式或最终一致性设计 —— Iris 不提供分布式事务支持,也绝不该提供。











