4.5 目录结构设计
概述
目录结构是架构决策的物理体现。本节解释"为什么这样放"——平铺单包、snake_case 命名、internal 边界、utility 无业务依赖等约定背后的原因。完整目录树见 1.3 项目目录结构。
后端顶层布局与设计意图
text
main.go 入口极薄:注册五库驱动 + Redis 适配器 → cmd.Run
internal/ 私有包边界(Go internal 语义:外部不可 import)
cmd/ 启动编排独立成包——把 main 的"做事"与"声明"分离
api/v1/ 契约层放 internal 外?——不,本项目放顶层 api/,
便于生成器与 gf 工具链定位,也表达"对外契约"语义
controller/ logic/ service/ dao/ 四层各自单包平铺
middleware/ 中间件 + wire.go 函数变量接线(不 import 业务包)
migrate/ 迁移引擎独立——gdb 裸连,必须与 dao 体系隔离
model/hooks/ 全局钩子独立于 entity/do(生成物与手写物分离)
router/ router.go(注册)+ bind.go(反射)+ meta/(元数据表)
task/ 定时任务实现——单向依赖 logic,cmd 空白导入触发
templates/ 生成器模板独立于 internal(模板演进与代码解耦)
utility/ 基础能力包——无业务依赖,可供任何层使用
manifest/ 配置与数据(ip2region.xdb)
resource/ 静态资源(SetServerRoot 提供 /upload/**、swagger 页面)
hack/ 工具链配置(gf gen dao 模板、golden 回归基线)平铺 vs 分目录的取舍
| 维度 | 单包平铺(选择) | 按域分目录(弃用) |
|---|---|---|
| 跨模块复用 | 同包直接调用包级函数,零 import、无循环依赖风险 | 需 import,深组合易循环 |
| 新增模块 | 4 个文件各就各位,无目录推断 | 多级目录间跳转 |
| 生成器 | 目标路径唯一确定 | 需推断层级 |
| init 注册 | 同包全量加载,无需 blank import | 每包需 blank import |
| 代价 | 单包文件多(30+) | 包粒度细 |
Go 编译器对大包无压力,而"跨模块直接调用"的收益在中后台系统里极高(city 编解码、身份加载、字典文本被大量模块复用)。
文件命名规范
text
后端(对齐 dao 产物):表名去 goframe_ 前缀的 snake_case
api/v1/config_item.go · internal/controller/config_item.go
internal/logic/config_item.go · internal/service/config_item.go
前端:驼峰
src/api/data/config.ts · src/views/data/configItem/
原因:后端文件名与 dao/entity/do 生成物一致,生成器与人工开发心智统一;
前端沿用 JS 社区驼峰惯例,两边各自从俗。internal 的边界价值
internal/ 是 Go 编译器强制的私有边界——仓库外代码无法 import,防止外部项目把实现细节当公共 API 依赖。分层红线(controller 不 import dao 等)靠 code review + 生成器模板保证,internal 防的是"仓库外扩散"。
utility 的纪律
text
utility/* 只依赖标准库与三方库,禁止 import internal(防反向耦合)
cache 统一缓存门面(Redis 优先、内存降级)
captcha 验证码生成/校验
crypto bcrypt + salt
excel excelize 封装(ReadSheet/WriteSheet)
ip2region xdb 归属地(懒加载 + 读锁)
jwt HS256 双令牌 Sign/Parse
response 统一信封写出
ua OS/Browser 解析
upload 两段式上传(SaveTemp/SaveFromTemp/GetFileUrl)前端目录设计
text
views 按"域"分目录(system/data/content/monitor/tool…)与后端模块域对应;
api 同样按域分目录;
components/store/utils/hooks 不按域——全局共享层;
settings/styles/enums 全局配置面。views 域归属与生成器 genDomainMap 一致(user/role→system,config→data,article→content,operation_log→monitor,example→tool,默认 tool),生成器据此决定前端产出路径。