Go项目用Echo框架应按业务域而非技术层划分包,如internal/users下含handler、service等文件,对外仅暴露RegisterRoutes();router/只聚合路由注册并挂载中间件,不涉业务逻辑;中间件需强类型context传值,避免interface{}断言panic。

Go 项目用 Echo 框架时,结构混乱不是框架问题,而是目录组织没守住边界——一旦把 handler、service、model 全塞进一个包,20 个接口后就没人敢改权限逻辑了。
按业务域而非技术层划分包名
很多人照搬 Gin 或旧项目习惯,建 handlers、services、models 三层平铺目录,结果所有用户相关逻辑散落在三个包里,加个“邮箱唯一性校验”要跳转 4 个文件。真正的约束是:每个业务域(如 users、orders、payments)应自成闭环包,内部再按职责分小结构。
- 推荐结构:
internal/users下含handler.go、service.go、repository.go、model.go,对外只暴露RegisterRoutes()函数 - 禁止跨域引用:users 包不能 import payments 包的 model;若需共享数据结构(如 ID 类型),提至
internal/types,且仅放类型定义,不放方法 - 路由注册必须封装:不要在
main.go里直接写e.POST("/users", users.Create),而是users.RegisterRoutes(e.Group("/api/v1"))
router/ 和 internal/ 的分工必须明确
router/ 目录只做一件事:聚合各业务包的 RegisterRoutes 调用,并挂载中间件。它不定义任何 handler,也不碰业务逻辑。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
-
router/router.go中只出现:users.RegisterRoutes(v1)、orders.RegisterRoutes(v1)、v1.Use(middleware.JWT()) - 绝对不要在
router/里写c.Bind(&u)或调用 service 方法——那是internal/users/handler.go的事 - 版本分组必须由 router 层控制:
v1 := e.Group("/api/v1"),而不是让每个业务包自己搞e.Group("/api/v1/users")
middleware/ 里别传原始 context
用 c.Set("user_id", 123) 看似快,但后续 handler 里 c.Get("user_id") 返回 interface{},强制类型断言易 panic,IDE 也无法跳转。
立即学习“go语言免费学习笔记(深入)”;
- 正确定义 key:
type userIDKey string; const userIDKey userCtxKey = "user_id" - 封装取值函数:
func GetUserID(c echo.Context) (int64, bool) { v := c.Get(userIDKey); id, ok := v.(int64); return id, ok } - 中间件中赋值必须用强类型:
c.Set(userIDKey, id),避免拼错 key 字符串 - 别把 auth 中间件和日志中间件混在一个文件里——按用途拆:auth.go、logging.go、rate_limit.go
最常被忽略的是:router 层的 Group 前缀和业务包内 handler 的路径拼接关系。比如 users.RegisterRoutes(e.Group("/api/v1")) 传入的是带前缀的 group,那 handler 里就该写 g.POST("/register", h.Register),而不是 g.POST("/api/v1/users/register", ...)——重复拼接会导致路由错位,调试时 e.Debug = true 也看不出问题。

















