3.1 KiB
3.1 KiB
tags, aliases, date created, date modified
| tags | aliases | date created | date modified | |
|---|---|---|---|---|
|
星期三, 十二月 10日 2025, 11:08:17 上午 | 星期三, 十二月 10日 2025, 11:08:58 上午 |
这是一个非常有前瞻性的问题。你现在的顾虑完全符合 “高内聚、低耦合” 的架构直觉。
在企业级 Go 项目中,如果把所有中间件(Auth, CORS, RateLimit, Logger, Recovery, Trace…)都堆在 internal/middleware/ 根目录下,确实会变成一个“大杂烩”,导致文件结构不明晰。
基于 Modular Clean Architecture 和 Go 最佳实践,我推荐采用 “按复杂度分层 (Complexity-Based Layering)” 的混合策略。
🏗️ 推荐的中间件目录演进策略
我们不需要一开始就过度设计(Over-engineering),而是制定一条规则:
- 通用基础设施 (Universal Infra): 逻辑简单、无状态、单文件即可解决的,直接放在根目录。
- 复杂业务逻辑 (Complex Logic): 需要定义配置结构体、辅助函数、或依赖外部库较重的,建立子目录。
修正后的目录蓝图
internal/
└── middleware/
│
│ # --- Level 1: 通用基础设施 (直接放根目录) ---
├── recovery.go # [现有] 及其简单,就一个函数
├── not_found.go # [现有] 就一个 Handler
├── cors.go # [未来] CORS 配置通常单文件搞定
├── trace.go # [未来] 注入 TraceID
│
│ # --- Level 2: 复杂中间件 (独立子包) ---
├── auth/ # [未来] 认证模块很复杂
│ ├── jwt.go # JWT 解析逻辑
│ └── context.go # 将 UserID 注入 Context 的辅助函数
│
├── ratelimit/ # [未来] 限流可能涉及 Redis 脚本
│ ├── ip_limiter.go
│ └── lua_script.go
│
└── log/ # [未来] 访问日志
└── access_logger.go
💡 为什么要这样设计?(Rationale)
1. Go 的包导入体验 (Import DX)
-
对于通用中间件:
我们在 main.go 里的调用通常希望长这样:
// 读起来很通顺:使用中间件 -> 恢复、日志、CORS r.Use(middleware.Recovery()) r.Use(middleware.Logger()) r.Use(middleware.Cors())如果强行把
recovery.go放到middleware/recovery/recovery.go,调用就会变成recovery.New(),这在语义上丢失了“这是一个中间件”的上下文,而且引入了太多的包名。 -
对于复杂中间件:
认证通常需要配置:
// 读起来清楚:这是 Auth 相关的中间件 r.Use(auth.Middleware(auth.Config{…}))这时候引入
auth子包是合理的。
2. 避免循环依赖
recovery.go 和 not_found.go 属于基础设施的底座,它们几乎不依赖其他业务代码(除了我们刚才定义的 app 包)。将它们放在根目录,可以方便地被其他子包引用(虽然中间件之间很少互相引用)。