分层与依赖
标准 Go 布局,internal 按模块组织,严格三层:
internal/<module>/
router.go 路由注册 RegisterRoutes(r, prefix)
handler/ HTTP 层:绑定参数、调 service、返回 response.R
service/ 业务逻辑;system 的 service 导出供 auth 复用
repository/ 数据访问,只有这一层碰 GORM/DB
model/ entity(表) / dto(入参) / vo(出参)单向依赖
handler → service → repository → model- 禁止反向依赖。
- 禁止 handler 直连 repository。
pkg不能 importinternal。
例外:pkg 需要落库怎么办
注解层(如 pkg/oplog)需要落库,但 pkg 不能依赖 internal 的 service/repository,否则成环。解法是 Recorder 回调反向注册:
go
// pkg/oplog 只定义 Recorder 接口与 Event 组装
type Recorder func(ctx context.Context, evt *Event)
// cmd/standalone/main.go 启动时由 internal 反向注册实现
oplog.Init(systemservice.OperLogSvcApp.RecordOper)pkg 只组装 Event,由 internal/system 在进程启动时经 Init 反向注册 Recorder 消费。这正是原项目 LogAspect 发 OperLogEvent、SysOperLogServiceImpl @Async @EventListener 落库那套解耦的等价物。详见 pkg 参考 / oplog。
同理,审计字段填充(create_by / create_time / update_*)由 pkg/repository 的 GORM 回调做,但它只拿得到 *gorm.DB ,取不到 *gin.Context,故登录态经 context.Context 接力(WithAuditUser / AuditUserFrom)——由 loginhelper.AuditContext 中间件从 gin.Context 取登录用户写进 request.Context。详见 一次请求的流转。
命名约定
- 包名小写无下划线。
- 文件名
xxx_handler.go/xxx_service.go/xxx_repository.go。 - 实体对应表名(
SysUser→ 表sys_user)。GORMSingularTable:true,表名单数,实体无需TableName()。
跨模块复用矩阵
| 模块 | 有 repository 层 | model 子目录 | 复用方式 |
|---|---|---|---|
| auth | 否 | model/ + model/vo/ | auth 复用 system service(in-process) |
| system | 是(14 个 repo) | entity/bo/vo/dto 全套 | 导出 service 供 auth 复用 |
| monitor | 否 | model/vo/ | 直读 system repo + pkg/redis |
| resource | 是(3 个 repo) | entity/bo/vo/dto 全套 | 业务逻辑部分仍在 system service(如 message/box) |