Skip to content

目录结构

IMPORTANT

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

服务端

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

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

  1. 和一些知名项目略有不同的是,目录名称全部使用单数(包括 configscript
    • 首先是因为这是 Go 社区的命名惯例,比如 vendor、test,标准库也是清一色的单数命名
    • 其次就是为了保持风格统一,特别是 internal 下面的 handler、model、service 都是单数形式;很多项目外层是复数形式的 configs,内层却是 handler 的单数形式。
    • 以上命名方式都有自己的解释,但我认为最重要的还是 Go 社区给包命名的定义:包名描述的是"这个包是什么",而不是"这个包里装了什么",所以应该使用单数。
  2. 没有 common 目录,这是 Go 社区共识,因为它容易成为全局依赖洼地,极易诱发循环依赖,或者变成代码垃圾抽屉;转而使用 internal/kit(成套工具箱)、internal/infra(基础设施),外层还有不依赖任何基础设施的 pkgpkg/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 组件,反而导致一些问题。