Go
gin_demo · Gin + GORM + PostgreSQL

一个采用 Wire 依赖注入的生产级 Gin API 项目

该项目现在采用 cmd/ + internal/ 的生产级布局,使用 Wire 做编译期依赖注入, 用接口驱动业务层,用 GORM + 事务封装保证订单创建原子性,用 Zap 做结构化日志输出。 首页不仅提供 Swagger 入口,也解释为什么要这样组织项目。

Gin Wire Zap GORM PostgreSQL JWT Atlas Kamal

这个结构的好处

职责边界清晰入口、业务、基础设施、HTTP 适配层分离,阅读和定位代码更快。
更容易单测业务服务依赖接口而不是具体实现,mock 仓储和 token 组件更方便。
依赖可视化Wire 在编译期生成装配代码,依赖链显式、稳定,减少隐藏耦合。
更适合演进后续替换日志、数据库实现、缓存或消息队列时,对业务层影响更小。
和旧结构相比,最大的变化不是“文件夹变多”,而是:对象不再自己创建依赖,而是由顶层统一装配;业务层只关心业务,不再关心怎么 new 下游对象。

依赖注入与 Wire 是怎么工作的

cmd/api/main.go
    -> InitializeApplication()
    -> Wire 生成装配代码
    -> Config / Logger / DB / Store / JWT Manager / Password Hasher
    -> Services
    -> Handlers
    -> HTTP Router
    -> Application.Run()
Wire 不是运行时容器。它会根据构造函数签名在编译期生成普通 Go 代码。好处是没有反射开销、依赖关系一眼可查、少写样板装配代码。当前项目里,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/applicationApplication 对象,统一持有配置、路由、日志、数据库资源方便集中管理生命周期,如启动、关闭、资源释放。
internal/config配置加载与环境变量解析避免配置逻辑散落在各层,便于统一管理。
internal/domain/model领域模型,如用户、商品、订单、地址把“业务对象长什么样”与“怎么访问数据库”分开。
internal/application/dto请求/响应对象隔离 HTTP 输入输出与内部领域模型,减少直接暴露数据库模型。
internal/application/port接口定义,按聚合根拆分(user_repository、product_repository 等),以及 Token、Store、密码哈希每个聚合根一个文件,职责清晰,新增聚合只需添加新文件;让业务层面向抽象,天然利于单测和替换实现。
internal/application/service业务用例实现这里是核心业务层,只依赖接口,不依赖具体技术细节。
internal/infrastructure/loggingZap 日志组件把日志实现与业务分离,后续替换日志库更轻松。
internal/infrastructure/persistence/database数据库连接创建连接池、驱动、参数集中管理。
internal/infrastructure/persistence/gormstore仓储接口的 GORM 实现与事务封装把“接口”与“具体 ORM”隔离,方便 mock 或切换实现。
internal/infrastructure/security/jwtJWT 生成和解析让鉴权能力变成可注入组件,而不是全局工具函数。
internal/infrastructure/security/password密码哈希与校验把加密细节藏在接口实现里,业务层只调用 Hasher。
internal/transport/http/handlerGin Handler只负责参数绑定、调用 service、返回响应。
internal/transport/http/middleware鉴权、日志、跨域等中间件把横切逻辑统一放在传输层。
internal/transport/http/response统一响应结构避免每个 handler 自己拼 JSON 结构。
internal/transport/http/router路由注册集中路由定义,避免入口层和 handler 层互相污染。
config应用配置与 Kamal 部署配置config.yaml 给应用读取,deploy.yml 给 Kamal 用。
docsSwagger 生成产物文档与业务代码分离,发布时直接可用。
migrations历史 SQL 迁移文件保留数据库演进轨迹,便于排查和对比。
web首页静态资源提供项目说明页和入口导航,不影响 API 分层。

Nginx 与 Traefik 性能对比

性能维度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 标签自动生成路由
QPS 参考说明:以上数据基于 4 vCPU / 8GB RAM / SSD / 1Gbps 内网带宽的云主机、wrk 压测、后端为简单 Go 应用的典型值。不同硬件、TLS 算法、Keepalive 策略差异很大,仅供参考。实际选型建议:如果追求极致流量吞吐和静态资源服务,Nginx 仍是首选;如果团队以容器化部署为主、希望自动化证书和动态服务发现,Traefik 的运维收益更高。Kamal 体系里承担代理角色的是基于 Traefik 的 kamal-proxy,能力重点是部署配套而非流量极限性能

Kamal 与 Nginx 对比

能力NginxKamal(Traefik)
反向代理kamal-proxy 承担
HTTPS / SSL通常手动配证书或配合 Certbotkamal-proxy 自动申请与续期
零停机部署需要自己编排 Kamal 原生支持滚动切换
静态文件服务✅ 强项不是核心强项
缓存 / 高性能代理偏向部署编排,不以极致代理性能为目标
运维复杂度配置自由度高,但需要手工维护 对中小团队更省心
适合场景高性能静态资源、精细代理控制应用发布、容器化部署、快速上线
如果问“Kamal 里哪一块最像 Nginx”,答案是 kamal-proxy。它负责 80/443 入口、反向代理、TLS 证书和请求转发;而 Kamal 本身更偏向部署编排、版本切换和回滚。

部署选型怎么理解

Nginx 更擅长流量层适合静态资源、细粒度反向代理、缓存和极致性能场景。
Kamal 更擅长发布层适合容器化部署、版本切换、回滚和自动化上线流程。
kamal-proxy 类似网关层它是 Kamal 体系里最接近 Nginx 的部分,但能力重点是服务发布配套而不是全功能 Web 网关。
不是互斥关系很多生产环境里,外层仍可有网关,内部发布流程再交给 Kamal。

Atlas vs migrate

维度Atlasgo-migrate / SQL migrate
迁移思路声明式,围绕目标 schema 演进顺序式,按 up/down 脚本执行
适合团队协作 更容易对齐结构状态依赖严格维护脚本顺序
Diff 能力 可直接比较当前库与目标 schema通常手写迁移脚本,不直接做 schema diff
回滚模型偏向重新对齐目标状态 up/down 语义更直接
适合场景中长期维护、多人协作、持续演进流程简单、脚本清晰、传统迁移链路
本项目当前主线✅ Atlas保留历史迁移文件作参考与兼容
本项目当前以 schema.hcl + Atlas 为主线。实际使用时,可以先看 diff,再执行迁移;而 Kamal 部署场景下,迁移由发布流程里的 migrate 服务自动完成。

如何测试接口

  • 点击顶部 打开 Swagger,进入在线接口调试页面。
  • 先调用 /api/v1/register 注册,再调用 /api/v1/login 获取 JWT。
  • 拿到 token 后,在 Swagger 顶部点击 Authorize,填入 Bearer <token>
  • 之后即可测试用户、商品、订单相关受保护接口。