项目结构 #

一、项目结构概述 #

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