Beego中大型项目需按职责拆分结构:controller仅处理HTTP流转,业务逻辑移至services/usecases;路由统一收口router.go;模型分domain、repository、orm三层;配置用环境变量;静态资源用{{asset}}函数;main.go只保留启动核心逻辑。

Beego 默认项目结构够用,但直接照搬 bee new 生成的目录在中大型项目里很快会失控——控制器膨胀、模型耦合、路由散落、配置难维护。真正需要的不是“标准结构”,而是能支撑迭代、隔离变更、便于测试的组织逻辑。
controller 层不能只放 handler 函数
把所有 HTTP 处理逻辑塞进 controllers/ 下的 struct 方法,是新手最常踩的坑。一旦业务变复杂,UserController 里混着权限校验、参数转换、第三方调用、事务控制,就很难复用或单元测试。
- 按职责拆分:把非 HTTP 相关逻辑(如用户状态变更、积分计算)抽到
services/或usecases/目录,控制器只做「接收请求 → 调用 usecase → 返回响应」三件事 - 避免直接操作
beego.Controller的字段(如c.Data、c.Ctx)做业务判断;改用显式参数传递,方便脱离 HTTP 环境测试 - RESTful 路由注册别写在
main.go或全局init();统一收口到router.go,并按模块分组(如userRouter()、orderRouter())
model 层不等于数据库表映射
models/ 目录如果只放 orm.RegisterModel() 的结构体,等于把数据访问层和领域模型绑死。后续加缓存、换存储、做读写分离时,修改成本极高。
- 区分三层:领域模型(domain/model)、数据访问对象(infrastructure/repository)、ORM 实体(infrastructure/orm);后两者都放在
infrastructure/下,与 domain 解耦 - 禁止在 controller 或 service 中直接调用
orm.QueryTable();全部封装进 repository 接口,用依赖注入传入 - 数据库迁移脚本不要硬编码在
models.go里;用bee generate migration独立管理,版本化提交
conf 和 static 目录的隐藏风险
conf/app.conf 看似只是配置文件,但实际运行时会被 beego 自动加载为全局变量,导致环境变量覆盖失效、热重载不生效、多实例共享配置等问题。
- 敏感配置(如数据库密码、API key)不要写进
app.conf;改用环境变量 +os.Getenv()显式读取,或通过config.NewConfig()动态加载外部配置中心 -
static/下的资源路径必须和beego.BConfig.WebConfig.StaticDir完全一致;常见错误是前端引用/js/app.js,但配置里写成static: "assets",结果 404 却查不到原因 - 模板中引用静态资源时,别用硬编码路径;用
{{asset "js/app.js"}}这类自定义 template func,后期可自动加 hash 或 CDN 前缀
main.go 不该是“胶水代码”堆积地
很多团队把中间件注册、日志初始化、ORM 连接、定时任务启动全塞进 main.go 的 main() 函数里,导致启动逻辑不可测、不可替换、难以调试。
-
main.go只保留三件事:解析命令行参数、调用app.Run()、捕获 panic;其余初始化逻辑下沉到app/或cmd/子包 - ORM 初始化必须显式调用
orm.RegisterDriver()和orm.RegisterDataBase(),不能依赖 beego 自动扫描;否则测试环境容易连错库 - 任务调度(如 cron)不要用
beego.Task全局注册;改用独立的task/包 +github.com/robfig/cron/v3,避免和 HTTP 生命周期强耦合
最易被忽略的一点:beego 的 “约定优于配置” 在小项目里是甜头,在大项目里是枷锁。目录结构本身不重要,重要的是每个目录下代码的职责边界是否清晰、能否被独立编译测试、变更时影响范围是否可控。别让 bee new 生成的第一行代码,决定你半年后的重构成本。


















