Skip to content

目录结构 ​

IMPORTANT

我们极其重视目录结构的搭建,因为它定之前怎么定都行,而一旦定了,它会反过来管着你。所以项目的每一个目录创建,都经过深思熟虑。

服务端 ​

Golang、Gin 并无推荐或标准的目录结构,我们结合了多个 AI 大模型的方案,反复确认了 go web 项目的通用目录结构,并要求它们提供参考来源,如 github 链接等,人工确认整理 top20 高星项目,最终得到了一份:完全贴合 Go 社区风格 + 企业级落地标准的目录结构设计。

设计随开发过程持续完善多次,主要特点如下:

  1. 和一些知名项目略有不同的是,目录名称全部使用单数(包括 config 和 script)
    • 首先是因为这是 Go 社区的命名惯例,比如 vendor、test,标准库也是清一色的单数命名
    • 其次就是为了保持风格统一,特别是 internal 下面的 handler、model、service 都是单数形式;很多项目外层是复数形式的 configs,内层却是 handler 的单数形式。
    • 以上命名方式都有自己的解释,但我认为最重要的还是 Go 社区给包命名的定义:包名描述的是"这个包是什么",而不是"这个包里装了什么",所以应该使用单数。
  2. 没有 common 目录,这是 Go 社区共识,因为它容易成为全局依赖洼地,极易诱发循环依赖,或者变成代码垃圾抽屉;转而使用 internal/kit(成套工具箱)、internal/infra(基础设施),外层还有不依赖任何基础设施的 pkg 和 pkg/util 等粒度更细、更明确的包名代替。
bash
AI GO ADMIN
├─asset 一些私有静态资源文件
│  ├─captcha
│  │  └─click 点选验证码的图标、背景等
│  │              
│  └─font 预设字体,目前用于点选验证码的中文渲染
│          
├─cmd
│  │  main.go 程序主入口
│  │  
│  ├─migrate 数据库迁移命令实现
│  │  │  commands.go
│  │  │  migrate.go
│  │  │  prefix.go
│  │  │  
│  │  └─migrations 数据库迁移文件,为避免系统开始就有很多迁移,并没有一个数据表一个迁移文件,且和模型文件(/model)对应
│  │          000001_admin.down.sql
│  │          000001_admin.up.sql
│  │          000002_common.down.sql
│  │          000002_common.up.sql
│  │          000003_seed.down.sql
│  │          000003_seed.up.sql 种子数据
│  │          
│  └─serve
│          serve.go API 服务入口
│          
├─config 配置目录
│      captcha.yaml 验证码配置
│      config.yaml 未分类的综合配置
│      cors.yaml 跨域配置
│      upload.yaml 文件上传配置
│      
├─internal
│  ├─dto 数据传输对象 DTO
│  │      
│  ├─handler 控制器
│  │  │  base.go 基控制器实现
│  │  │  
│  │  ├─admin 后台控制器
│  │  │          
│  │  └─common 公共控制器
│  │          
│  ├─infra 基础设施
│  │  ├─captcha
│  │  │      click.go 点选验证码
│  │  │      
│  │  ├─config
│  │  │      config.go 配置加载,它导出了全局配置单例
│  │  │      
│  │  ├─database
│  │  │      database.go 数据库连接,它导出了全局数据库连接句柄(但真正使用句柄时,一般从基仓储取)
│  │  │      
│  │  ├─permission
│  │  │      admin.go 管理员鉴权实现
│  │  │      
│  │  ├─token
│  │  │  │  token.go 身份令牌生成于验证等
│  │  │  │  
│  │  │  └─driver 身份令牌多驱动支持
│  │  │          
│  │  └─upload
│  │      │  upload.go 文件上传
│  │      │  
│  │      └─driver 上传多驱动支持
│  │              
│  ├─kit 成套辅助工具
│  │  ├─bindx
│  │  │      bindx.go 数据绑定
│  │  │      
│  │  ├─httpx
│  │  │      context.go HTTP 上下文相关
│  │  │      response.go 创建 HTTP 响应
│  │  │      
│  │  └─urlx
│  │          urlx.go URL 相关,目前主要是 FullURL(获取静态资源完整路径)
│  │          
│  ├─middleware
│  │      admin_auth.go 管理员认证中间件
│  │      admin_log.go 管理员日志中间件
│  │      admin_permission.go 管理员验权中间件
│  │      cors.go 全局跨域中间件
│  │      
│  ├─model 数据模型,和迁移一一对应;一个文件内可能含有多个模型是为了避免一开始就有很多文件
│  │      admin.go
│  │      common.go
│  │      
│  ├─repository 仓储
│  │  │  base.go 基仓储实现
│  │  │  
│  │  ├─admin 后台仓储
│  │  │          
│  │  └─common 公共仓储
│  │          
│  ├─router 路由注册,所有路由注册定义均在此目录内
│  │  │  index.go
│  │  │  router.go
│  │  │  static.go 静态资源路由
│  │  │  
│  │  ├─admin 后台路由注册
│  │  │          
│  │  ├─common 公共路由注册
│  │  │      
│  │  └─registry 公共路由注册方法
│  │          
│  └─service 服务
│      │  base.go 基服务
│      │  
│      ├─admin 后台服务
│      │          
│      └─common 公共服务
│              
├─pkg 公共库代码(可被其他项目引用,以 x 结尾的它是表示对应包的扩展,比如 airx 就是 air 的扩展)
│  ├─airx
│  │
│  ├─copierx
│  │      
│  ├─filesystem 文件系统相关
│  │      
│  ├─jsonx
│  │      
│  ├─random 随机数生成
│  │      
│  ├─structx
│  │      
│  ├─timex
│  │      
│  ├─tree 树状数据生成
│  │      
│  ├─util
│  │      ptr.go 指针工具
│  │      str.go 字符串工具
│  │      
│  └─xss 反 XSS 工具
│          
└─static
│  ├─images 静态文件
│  │      
│  └─upload 默认上传目录
│  .air.toml Air 配置
│  .env.yaml.example 环境配置示例(改名为 .env.yaml 后使用,编译不嵌入二进制,同环境变量一样可覆盖同名配置项)
│  .gitignore GIT 忽略配置
│  AGENTS.md 智能体工作指导
│  embed.go 用于内嵌一些运行时资源
│  go.mod
│  go.sum
│  LICENSE
│  README.md

前端 ​

bash
WEB 使用经典目录结构设计,并合理优化
├─src
│  │  App.vue
│  │  main.ts
│  │  
│  ├─api 存放所有接口请求函数的目录
│  │  │  common.ts
│  │  │  table.ts
│  │  │  
│  │  └─admin 后台接口请求函数
│  │              
│  ├─assets 静态资源
│  │          
│  ├─components 组件
│  │  ├─agInput AIGO 输入组件
│  │  │  │  helper.ts
│  │  │  │  index.ts
│  │  │  │  index.vue 输入组件入口,可以向它传递 type 渲染任意输入组件,但仅建议在动态表单的场景使用
│  │  │  │  
│  │  │  └─components
│  │  │      │  agUpload.vue 文件上传
│  │  │      │  areaSelect.vue 省份地区选择
│  │  │      │  array.vue 数组
│  │  │      │  editor.vue 富文本编辑器
│  │  │      │  iconSelect.vue 图标选择
│  │  │      │  remoteSelect.vue 远程下拉
│  │  │      │  
│  │  │      └─editor 富文本编辑器目录,一个文件等于一个编辑器实现
│  │  │              
│  │  ├─clickCaptcha 点选验证码
│  │  │      
│  │  ├─contextmenu 右击菜单
│  │  │      
│  │  ├─icon 图标
│  │  │      
│  │  └─table 表格组件
│  │      │  index.ts
│  │      │  index.vue 表格入口组件
│  │      │  types.ts
│  │      │  
│  │      ├─cellRenderer 表格单元格渲染器,一个文件等于一个渲染器,系统自动加载
│  │      │      buttons.vue
│  │      │      color.vue
│  │      │      customRender.vue
│  │      │      customTemplate.vue
│  │      │      datetime.vue
│  │      │      default.vue
│  │      │      icon.vue
│  │      │      image.vue
│  │      │      images.vue
│  │      │      switch.vue
│  │      │      tag.vue
│  │      │      tags.vue
│  │      │      url.vue
│  │      │      
│  │      └─header 表格表头组件
│  │              comSearch.vue
│  │              index.vue
│  │              
│  ├─hooks
│  │      useDark.ts 暗黑模式相关
│  │      useGlobalProperties.ts 全局 vue 实例相关
│  │      useTableManager.ts 表格管家
│  │      
│  ├─lang 多语言
│  │  │  index.ts
│  │  │  
│  │  ├─en 英文语言包
│  │  │          
│  │  └─zh-cn 中文语言包
│  │              
│  ├─layouts 布局
│  │  ├─admin 后台布局
│  │  │          
│  │  └─common 公共布局
│  │              
│  ├─router
│  │  │  index.ts
│  │  │  static.ts 静态路由加载
│  │  │  
│  │  └─static 静态路由目录,一个文件一组静态路由,可自动发现和加载
│  │          adminBase.ts 后台静态路由
│  │          
│  ├─stores 状态商店
│  │  │  adminInfo.ts 管理员信息
│  │  │  config.ts 配置数据(布局配置、多语言、站点配置)
│  │  │  index.ts
│  │  │  menu.ts 后台菜单数据
│  │  │  navTab.ts 后台导航栏数据
│  │  │  ref.ts 全局引用句柄
│  │  │  
│  │  ├─constant 全局常量定义
│  │  │      cacheKey.ts
│  │  │      common.ts
│  │  │      
│  │  └─interface 状态商店接口定义
│  │          config.ts
│  │          index.ts
│  │          
│  ├─styles 样式
│  │      app.scss 全局样式
│  │      dark.scss 黑暗模式样式
│  │      element.scss Element Plus 的样式覆盖
│  │      index.scss 统一入口,main.ts 只导入它
│  │      loading.scss 全局 Loading 样式
│  │      mixins.scss mixins 与 function 定义
│  │      var.scss scss 变量定义
│  │      
│  ├─utils 工具
│  │      common.ts 公共工具(前端不存在依赖循环,可以用 common)
│  │      dev.ts 开发环境辅助
│  │      horizontalScroll.ts 横向滚动条
│  │      layout.ts 布局相关工具
│  │      loading.ts 全局 loading
│  │      mask.ts 全局 mask
│  │      random.ts 随机数生成
│  │      request.ts 请求封装(Axios)
│  │      router.ts 路由相关,如动态路由注册
│  │      storage.ts 浏览器存储(Local 和 Session)
│  │      validate.ts 表单数据验证
│  │      vite.ts Vite 相关工具
│  │      
│  └─views 视图(vue 页面组件)
│      │  index.vue 首页
│      │  
│      ├─admin 后台页面
│      │              
│      └─common 公共页面
│                  
└─types 全局类型定义
│  .editorconfig IDE 风格统一配置
│  .env 基础环境变量定义
│  .env.development 开发环境变量定义
│  .env.production 生产环境变量定义
│  .prettierignore prettier 忽略
│  .prettierrc.js prettier 配置
│  eslint.config.js eslint 配置
│  index.html 入口文件
│  package.json
│  pnpm-lock.yaml
│  tsconfig.json ts 配置
│  vite.config.ts vite 配置

顺便一提,前端的命名规范很有意思,因为每个厂都有自己的规范。比如:

  • 目录名使用 kebab-case,但组件的目录名又随组件的文件名使用 PascalCase;
  • hooks 文件名使用 kebab-case,但 tools 文件名应该使用 camelCase;
  • 全局类型定义使用 types/kebab-case.ts,但单个组件类型定义文件名随组件名使用 PascalCase.types.ts

等等花里胡哨的设计,如果你要统计 top20 的高星仓库,其结果可能会让你头大如斗,这太乱了,稍微不注意就会用错。

  • 那么目录名大小写乱用,有影响吗?没影响
  • 组件小写开头,影响导入、影响编译吗?不影响
  • 全局类型定义文件名使用 camelCase 有影响吗?也没有影响

过度区分命名格式会显著增加团队协作成本、增加出错概率,换为以下规范:

对象命名格式
Interface / Type 名PascalCase
classPascalCase
其余(组件可选的导入为 PascalCase)camelCase

在 vue 中,只要你能写对导入路径,那么代码就能正常运行,以上这版 简单粗暴 的命名方式我已经使用几年了,没有出现过任何因为命名产生的问题,实际证明统一使用 camelCase 既不影响功能实现,又能显著降低心智负担,看着也非常舒服。

另外,我知道 vue 官方文档写了组件名推荐 PascalCase 或者 kebab-case,不过这是为了保持和 html 的标签一样的命名方式,很多人还会将 kebab-case 的组件文件名,全局注册为 PascalCase 组件,反而导致一些问题。