项目结构 #
一、项目结构概述 #
1.1 为什么需要良好的项目结构 #
| 优势 | 说明 |
|---|---|
| 可维护性 | 代码组织清晰,易于维护 |
| 可扩展性 | 方便添加新功能 |
| 可测试性 | 便于编写单元测试 |
| 团队协作 | 统一规范,提高效率 |
| 代码复用 | 减少重复代码 |
1.2 常见项目结构 #
text
简单结构 分层结构 企业级结构
├── main.go ├── cmd/ ├── cmd/
├── go.mod ├── internal/ ├── internal/
└── go.sum ├── pkg/ ├── pkg/
├── config/ ├── api/
└── go.mod ├── config/
└── deployments/
二、简单项目结构 #
2.1 目录结构 #
text
simple-demo/
├── main.go # 程序入口
├── go.mod # 模块定义
├── go.sum # 依赖校验
├── handlers/ # 处理函数
│ └── user.go
├── middleware/ # 中间件
│ └── auth.go
├── models/ # 数据模型
│ └── user.go
├── config/ # 配置
│ └── config.go
└── utils/ # 工具函数
└── response.go
2.2 代码示例 #
main.go:
go
package main
import (
"simple-demo/config"
"simple-demo/handlers"
"simple-demo/middleware"
"github.com/gin-gonic/gin"
)
func main() {
// 加载配置
cfg := config.Load()
// 创建路由
r := gin.Default()
// 注册中间件
r.Use(middleware.Logger())
// 注册路由
r.GET("/users", handlers.GetUsers)
r.POST("/users", handlers.CreateUser)
// 启动服务
r.Run(cfg.Port)
}
handlers/user.go:
go
package handlers
import (
"net/http"
"simple-demo/models"
"github.com/gin-gonic/gin"
)
func GetUsers(c *gin.Context) {
users := models.GetAllUsers()
c.JSON(http.StatusOK, users)
}
func CreateUser(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := models.CreateUser(&user); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, user)
}
models/user.go:
go
package models
type User struct {
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name"`
Email string `json:"email"`
}
var users []User
func GetAllUsers() []User {
return users
}
func CreateUser(user *User) error {
users = append(users, *user)
return nil
}
三、分层项目结构 #
3.1 目录结构 #
text
layered-demo/
├── cmd/ # 命令行入口
│ └── api/
│ └── main.go
├── internal/ # 私有应用代码
│ ├── handler/ # 处理层(控制器)
│ │ └── user.go
│ ├── service/ # 业务逻辑层
│ │ └── user.go
│ ├── repository/ # 数据访问层
│ │ └── user.go
│ ├── model/ # 数据模型
│ │ └── user.go
│ └── middleware/ # 中间件
│ └── auth.go
├── pkg/ # 公共库
│ ├── config/
│ │ └── config.go
│ ├── logger/
│ │ └── logger.go
│ └── response/
│ └── response.go
├── config/ # 配置文件
│ └── config.yaml
├── docs/ # 文档
│ └── swagger/
├── scripts/ # 脚本
│ └── migrate.sh
├── go.mod
└── go.sum
3.2 分层架构 #
text
请求 → Handler → Service → Repository → 数据库
↓ ↓ ↓
处理请求 业务逻辑 数据访问
3.3 代码示例 #
cmd/api/main.go:
go
package main
import (
"layered-demo/internal/handler"
"layered-demo/internal/middleware"
"layered-demo/internal/repository"
"layered-demo/internal/service"
"layered-demo/pkg/config"
"github.com/gin-gonic/gin"
)
func main() {
// 加载配置
cfg, err := config.Load()
if err != nil {
panic(err)
}
// 初始化依赖
userRepo := repository.NewUserRepository(cfg.Database)
userService := service.NewUserService(userRepo)
userHandler := handler.NewUserHandler(userService)
// 创建路由
r := gin.Default()
// 注册中间件
r.Use(middleware.Logger())
r.Use(middleware.CORS())
// 注册路由
api := r.Group("/api/v1")
{
users := api.Group("/users")
{
users.GET("", userHandler.List)
users.GET("/:id", userHandler.Get)
users.POST("", userHandler.Create)
users.PUT("/:id", userHandler.Update)
users.DELETE("/:id", userHandler.Delete)
}
}
// 启动服务
r.Run(cfg.Server.Port)
}
internal/handler/user.go:
go
package handler
import (
"net/http"
"strconv"
"layered-demo/internal/model"
"layered-demo/internal/service"
"layered-demo/pkg/response"
"github.com/gin-gonic/gin"
)
type UserHandler struct {
service *service.UserService
}
func NewUserHandler(service *service.UserService) *UserHandler {
return &UserHandler{service: service}
}
func (h *UserHandler) List(c *gin.Context) {
users, err := h.service.List()
if err != nil {
response.Error(c, http.StatusInternalServerError, err.Error())
return
}
response.Success(c, users)
}
func (h *UserHandler) Get(c *gin.Context) {
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
user, err := h.service.Get(uint(id))
if err != nil {
response.Error(c, http.StatusNotFound, "User not found")
return
}
response.Success(c, user)
}
func (h *UserHandler) Create(c *gin.Context) {
var req model.CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, err.Error())
return
}
user, err := h.service.Create(&req)
if err != nil {
response.Error(c, http.StatusInternalServerError, err.Error())
return
}
response.Success(c, user)
}
func (h *UserHandler) Update(c *gin.Context) {
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
var req model.UpdateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, err.Error())
return
}
user, err := h.service.Update(uint(id), &req)
if err != nil {
response.Error(c, http.StatusInternalServerError, err.Error())
return
}
response.Success(c, user)
}
func (h *UserHandler) Delete(c *gin.Context) {
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
if err := h.service.Delete(uint(id)); err != nil {
response.Error(c, http.StatusInternalServerError, err.Error())
return
}
response.Success(c, nil)
}
internal/service/user.go:
go
package service
import (
"errors"
"layered-demo/internal/model"
"layered-demo/internal/repository"
)
type UserService struct {
repo *repository.UserRepository
}
func NewUserService(repo *repository.UserRepository) *UserService {
return &UserService{repo: repo}
}
func (s *UserService) List() ([]model.User, error) {
return s.repo.FindAll()
}
func (s *UserService) Get(id uint) (*model.User, error) {
return s.repo.FindByID(id)
}
func (s *UserService) Create(req *model.CreateUserRequest) (*model.User, error) {
if err := s.validateCreate(req); err != nil {
return nil, err
}
user := &model.User{
Name: req.Name,
Email: req.Email,
}
if err := s.repo.Create(user); err != nil {
return nil, err
}
return user, nil
}
func (s *UserService) Update(id uint, req *model.UpdateUserRequest) (*model.User, error) {
user, err := s.repo.FindByID(id)
if err != nil {
return nil, err
}
if req.Name != "" {
user.Name = req.Name
}
if req.Email != "" {
user.Email = req.Email
}
if err := s.repo.Update(user); err != nil {
return nil, err
}
return user, nil
}
func (s *UserService) Delete(id uint) error {
return s.repo.Delete(id)
}
func (s *UserService) validateCreate(req *model.CreateUserRequest) error {
if req.Name == "" {
return errors.New("name is required")
}
if req.Email == "" {
return errors.New("email is required")
}
return nil
}
internal/repository/user.go:
go
package repository
import (
"layered-demo/internal/model"
"gorm.io/gorm"
)
type UserRepository struct {
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) *UserRepository {
return &UserRepository{db: db}
}
func (r *UserRepository) FindAll() ([]model.User, error) {
var users []model.User
err := r.db.Find(&users).Error
return users, err
}
func (r *UserRepository) FindByID(id uint) (*model.User, error) {
var user model.User
err := r.db.First(&user, id).Error
if err != nil {
return nil, err
}
return &user, nil
}
func (r *UserRepository) Create(user *model.User) error {
return r.db.Create(user).Error
}
func (r *UserRepository) Update(user *model.User) error {
return r.db.Save(user).Error
}
func (r *UserRepository) Delete(id uint) error {
return r.db.Delete(&model.User{}, id).Error
}
四、企业级项目结构 #
4.1 目录结构 #
text
enterprise-demo/
├── cmd/ # 命令行工具
│ ├── api/
│ │ └── main.go # API服务入口
│ └── migrate/
│ └── main.go # 迁移工具
├── internal/ # 私有应用代码
│ ├── domain/ # 领域模型
│ │ ├── user/
│ │ │ ├── entity.go
│ │ │ ├── repository.go
│ │ │ └── service.go
│ │ └── order/
│ ├── application/ # 应用服务
│ │ └── service/
│ ├── infrastructure/ # 基础设施
│ │ ├── persistence/
│ │ ├── messaging/
│ │ └── cache/
│ └── interfaces/ # 接口层
│ ├── api/
│ │ └── handler/
│ └── grpc/
├── pkg/ # 公共库
│ ├── config/
│ ├── logger/
│ ├── middleware/
│ └── utils/
├── api/ # API定义
│ ├── openapi/
│ └── proto/
├── config/ # 配置文件
│ ├── config.yaml
│ └── config.example.yaml
├── deployments/ # 部署配置
│ ├── docker/
│ └── kubernetes/
├── scripts/ # 脚本
│ ├── build.sh
│ └── migrate.sh
├── docs/ # 文档
│ └── swagger/
├── test/ # 测试
│ ├── integration/
│ └── e2e/
├── go.mod
└── go.sum
4.2 DDD分层架构 #
text
Interfaces(接口层)
↓
Application(应用层)
↓
Domain(领域层)
↓
Infrastructure(基础设施层)
五、依赖注入 #
5.1 手动依赖注入 #
go
func main() {
// 初始化数据库
db := initDatabase()
// 初始化Repository
userRepo := repository.NewUserRepository(db)
// 初始化Service
userService := service.NewUserService(userRepo)
// 初始化Handler
userHandler := handler.NewUserHandler(userService)
// 注册路由
r := gin.Default()
r.GET("/users", userHandler.List)
r.Run()
}
5.2 使用Wire #
安装Wire:
bash
go install github.com/google/wire/cmd/wire@latest
wire.go:
go
//go:build wireinject
package main
import (
"github.com/google/wire"
"github.com/gin-gonic/gin"
)
func InitializeApp() (*gin.Engine, error) {
wire.Build(
config.Load,
database.NewDatabase,
repository.NewUserRepository,
service.NewUserService,
handler.NewUserHandler,
NewRouter,
)
return nil, nil
}
func NewRouter(
userHandler *handler.UserHandler,
middleware *middleware.Middleware,
) *gin.Engine {
r := gin.Default()
r.Use(middleware.Logger())
api := r.Group("/api/v1")
{
users := api.Group("/users")
{
users.GET("", userHandler.List)
users.POST("", userHandler.Create)
}
}
return r
}
生成代码:
bash
wire
六、配置管理最佳实践 #
6.1 多环境配置 #
text
config/
├── config.yaml # 默认配置
├── config.dev.yaml # 开发环境
├── config.test.yaml # 测试环境
└── config.prod.yaml # 生产环境
6.2 配置加载 #
go
package config
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Config struct {
Server ServerConfig
Database DatabaseConfig
Redis RedisConfig
}
func Load() (*Config, error) {
env := getEnv("APP_ENV", "dev")
viper.SetConfigName(fmt.Sprintf("config.%s", env))
viper.SetConfigType("yaml")
viper.AddConfigPath("./config")
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
var config Config
if err := viper.Unmarshal(&config); err != nil {
return nil, err
}
return &config, nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
七、总结 #
7.1 项目结构对比 #
| 结构 | 适用场景 | 复杂度 |
|---|---|---|
| 简单结构 | 小项目、学习 | 低 |
| 分层结构 | 中型项目 | 中 |
| 企业级结构 | 大型项目 | 高 |
7.2 最佳实践 #
| 实践 | 说明 |
|---|---|
| 分层设计 | Handler → Service → Repository |
| 依赖注入 | 解耦组件依赖 |
| 配置管理 | 多环境配置分离 |
| 错误处理 | 统一错误处理 |
| 日志规范 | 结构化日志 |
7.3 下一步 #
现在你已经了解了项目结构设计,接下来让我们学习 路由基础,深入了解Gin的路由系统!
最后更新:2026-03-28