该项目现在采用 cmd/ + internal/ 的生产级布局,使用 Wire 做编译期依赖注入,
用接口驱动业务层,用 GORM + 事务封装保证订单创建原子性,用 Zap 做结构化日志输出。
首页不仅提供 Swagger 入口,也解释为什么要这样组织项目。
cmd/api/main.go
-> InitializeApplication()
-> Wire 生成装配代码
-> Config / Logger / DB / Store / JWT Manager / Password Hasher
-> Services
-> Handlers
-> HTTP Router
-> Application.Run()
cmd/api/wire.go 只声明依赖图,真正参与编译的是 Wire 生成的代码。. ├── cmd/ │ └── api/ │ ├── main.go │ ├── wire.go │ └── wire_gen.go ├── internal/ │ ├── application/ │ │ ├── application.go │ │ ├── dto/ │ │ ├── port/ │ │ │ ├── user_repository.go │ │ │ ├── product_repository.go │ │ │ ├── order_repository.go │ │ │ ├── address_repository.go │ │ │ ├── store.go │ │ │ ├── token.go │ │ │ └── password.go │ │ └── service/ │ ├── config/ │ ├── domain/ │ │ └── model/ │ ├── infrastructure/ │ │ ├── logging/ │ │ ├── persistence/ │ │ │ ├── database/ │ │ │ └── gormstore/ │ │ └── security/ │ │ ├── jwt/ │ │ └── password/ │ └── transport/ │ └── http/ │ ├── handler/ │ ├── middleware/ │ ├── response/ │ └── router/ ├── config/ │ ├── config.yaml │ └── deploy.yml ├── docs/ ├── migrations/ ├── web/ ├── schema.hcl ├── docker-compose.prod.yml ├── Dockerfile ├── Makefile └── go.mod
| 目录 | 职责 | 为什么这样拆 |
|---|---|---|
cmd/api | 程序入口、Wire 装配、启动与关闭应用 | 让入口足够薄,只负责启动,不承载业务逻辑。 |
internal/application | Application 对象,统一持有配置、路由、日志、数据库资源 | 方便集中管理生命周期,如启动、关闭、资源释放。 |
internal/config | 配置加载与环境变量解析 | 避免配置逻辑散落在各层,便于统一管理。 |
internal/domain/model | 领域模型,如用户、商品、订单、地址 | 把“业务对象长什么样”与“怎么访问数据库”分开。 |
internal/application/dto | 请求/响应对象 | 隔离 HTTP 输入输出与内部领域模型,减少直接暴露数据库模型。 |
internal/application/port | 接口定义,按聚合根拆分(user_repository、product_repository 等),以及 Token、Store、密码哈希 | 每个聚合根一个文件,职责清晰,新增聚合只需添加新文件;让业务层面向抽象,天然利于单测和替换实现。 |
internal/application/service | 业务用例实现 | 这里是核心业务层,只依赖接口,不依赖具体技术细节。 |
internal/infrastructure/logging | Zap 日志组件 | 把日志实现与业务分离,后续替换日志库更轻松。 |
internal/infrastructure/persistence/database | 数据库连接创建 | 连接池、驱动、参数集中管理。 |
internal/infrastructure/persistence/gormstore | 仓储接口的 GORM 实现与事务封装 | 把“接口”与“具体 ORM”隔离,方便 mock 或切换实现。 |
internal/infrastructure/security/jwt | JWT 生成和解析 | 让鉴权能力变成可注入组件,而不是全局工具函数。 |
internal/infrastructure/security/password | 密码哈希与校验 | 把加密细节藏在接口实现里,业务层只调用 Hasher。 |
internal/transport/http/handler | Gin Handler | 只负责参数绑定、调用 service、返回响应。 |
internal/transport/http/middleware | 鉴权、日志、跨域等中间件 | 把横切逻辑统一放在传输层。 |
internal/transport/http/response | 统一响应结构 | 避免每个 handler 自己拼 JSON 结构。 |
internal/transport/http/router | 路由注册 | 集中路由定义,避免入口层和 handler 层互相污染。 |
config | 应用配置与 Kamal 部署配置 | config.yaml 给应用读取,deploy.yml 给 Kamal 用。 |
docs | Swagger 生成产物 | 文档与业务代码分离,发布时直接可用。 |
migrations | 历史 SQL 迁移文件 | 保留数据库演进轨迹,便于排查和对比。 |
web | 首页静态资源 | 提供项目说明页和入口导航,不影响 API 分层。 |
| 性能维度 | Nginx(典型值) | Traefik(典型值) |
|---|---|---|
| 实现语言 | C,事件驱动,内存占用极低 | Go,协程调度,内存占用相对较高 |
| 静态文件 QPS(10KB) | ✅ ~100,000+,sendfile + 零拷贝 | ~50,000 - 70,000,非设计核心 |
| 反向代理 QPS(HTTP 透传) | ✅ ~90,000+,长连接复用成熟 | ~40,000 - 60,000,动态路由开销略高 |
| 反向代理 QPS(HTTPS 透传) | ✅ ~25,000+ | ~18,000 - 25,000 |
| 容器生态集成 | 需配合 Consul/Nginx Plus 等实现动态 upstream | ✅ 原生,自动监听 Docker/K8s 标签变化 |
| 配置复杂度 | 自由度高,但需手写 location、upstream、rewrite | 声明式,基于 Provider 标签自动生成路由 |
kamal-proxy,能力重点是部署配套而非流量极限性能。| 能力 | Nginx | Kamal(Traefik) |
|---|---|---|
| 反向代理 | ✅ | ✅ 由 kamal-proxy 承担 |
| HTTPS / SSL | 通常手动配证书或配合 Certbot | ✅ 由 kamal-proxy 自动申请与续期 |
| 零停机部署 | 需要自己编排 | ✅ Kamal 原生支持滚动切换 |
| 静态文件服务 | ✅ 强项 | 不是核心强项 |
| 缓存 / 高性能代理 | ✅ | 偏向部署编排,不以极致代理性能为目标 |
| 运维复杂度 | 配置自由度高,但需要手工维护 | ✅ 对中小团队更省心 |
| 适合场景 | 高性能静态资源、精细代理控制 | 应用发布、容器化部署、快速上线 |
kamal-proxy。它负责 80/443 入口、反向代理、TLS 证书和请求转发;而 Kamal 本身更偏向部署编排、版本切换和回滚。| 维度 | Atlas | go-migrate / SQL migrate |
|---|---|---|
| 迁移思路 | 声明式,围绕目标 schema 演进 | 顺序式,按 up/down 脚本执行 |
| 适合团队协作 | ✅ 更容易对齐结构状态 | 依赖严格维护脚本顺序 |
| Diff 能力 | ✅ 可直接比较当前库与目标 schema | 通常手写迁移脚本,不直接做 schema diff |
| 回滚模型 | 偏向重新对齐目标状态 | ✅ up/down 语义更直接 |
| 适合场景 | 中长期维护、多人协作、持续演进 | 流程简单、脚本清晰、传统迁移链路 |
| 本项目当前主线 | ✅ Atlas | 保留历史迁移文件作参考与兼容 |
schema.hcl + Atlas 为主线。实际使用时,可以先看 diff,再执行迁移;而 Kamal 部署场景下,迁移由发布流程里的 migrate 服务自动完成。/api/v1/register 注册,再调用 /api/v1/login 获取 JWT。Bearer <token>。