项目结构 #

一、项目结构概述 #

1.1 为什么需要良好的项目结构 #

良好的项目结构带来以下好处:

好处 说明
可维护性 代码组织清晰,易于维护
可扩展性 方便添加新功能
可测试性 便于编写单元测试
团队协作 统一的代码组织方式
代码复用 避免重复代码

1.2 Fiber项目结构原则 #

text
1. 按功能模块组织
2. 分层架构设计
3. 依赖注入
4. 配置与代码分离
5. 统一的错误处理

二、基础项目结构 #

2.1 简单项目结构 #

适合小型项目或学习:

text
my-fiber-app/
├── main.go
├── handlers/
│   └── user.go
├── models/
│   └── user.go
├── go.mod
└── go.sum

2.2 标准项目结构 #

适合中型项目:

text
my-fiber-app/
├── cmd/
│   └── main.go
├── internal/
│   ├── handlers/
│   │   ├── user.go
│   │   └── auth.go
│   ├── models/
│   │   └── user.go
│   ├── services/
│   │   └── user.go
│   └── repositories/
│       └── user.go
├── pkg/
│   ├── config/
│   │   └── config.go
│   ├── middleware/
│   │   └── auth.go
│   └── utils/
│       └── response.go
├── config/
│   └── config.yaml
├── go.mod
├── go.sum
├── Makefile
└── README.md

三、企业级项目结构 #

3.1 完整项目结构 #

适合大型企业项目:

text
my-fiber-app/
├── cmd/
│   ├── api/
│   │   └── main.go
│   └── worker/
│       └── main.go
├── internal/
│   ├── domain/
│   │   ├── user/
│   │   │   ├── entity.go
│   │   │   ├── repository.go
│   │   │   └── service.go
│   │   └── product/
│   │       ├── entity.go
│   │       ├── repository.go
│   │       └── service.go
│   ├── handlers/
│   │   ├── user.go
│   │   ├── product.go
│   │   └── health.go
│   ├── middleware/
│   │   ├── auth.go
│   │   ├── logger.go
│   │   └── cors.go
│   └── infrastructure/
│       ├── database/
│       │   ├── postgres.go
│       │   └── redis.go
│       └── queue/
│           └── rabbitmq.go
├── pkg/
│   ├── config/
│   │   └── config.go
│   ├── logger/
│   │   └── logger.go
│   ├── validator/
│   │   └── validator.go
│   └── utils/
│       ├── response.go
│       └── jwt.go
├── api/
│   ├── openapi.yaml
│   └── docs/
├── configs/
│   ├── config.yaml
│   └── config.example.yaml
├── scripts/
│   ├── build.sh
│   └── deploy.sh
├── deployments/
│   ├── docker/
│   │   ├── Dockerfile
│   │   └── docker-compose.yaml
│   └── k8s/
│       └── deployment.yaml
├── test/
│   ├── integration/
│   └── e2e/
├── go.mod
├── go.sum
├── Makefile
└── README.md

3.2 目录说明 #

目录 说明
cmd 应用入口,可以有多个入口
internal 私有代码,不可被外部导入
pkg 公共代码,可被外部导入
api API文档
configs 配置文件
scripts 脚本文件
deployments 部署配置
test 测试文件

四、代码分层 #

4.1 分层架构 #

text
┌─────────────────────────────────────┐
│           Handlers (路由处理)         │
├─────────────────────────────────────┤
│          Services (业务逻辑)          │
├─────────────────────────────────────┤
│       Repositories (数据访问)         │
├─────────────────────────────────────┤
│          Models (数据模型)            │
└─────────────────────────────────────┘

4.2 Handler层 #

负责处理HTTP请求和响应:

go
// internal/handlers/user.go
package handlers

import (
    "github.com/gofiber/fiber/v2"
    "my-fiber-app/internal/services"
)

type UserHandler struct {
    service *services.UserService
}

func NewUserHandler(service *services.UserService) *UserHandler {
    return &UserHandler{service: service}
}

func (h *UserHandler) GetUsers(c *fiber.Ctx) error {
    users, err := h.service.GetAll()
    if err != nil {
        return c.Status(500).JSON(fiber.Map{
            "error": err.Error(),
        })
    }
    return c.JSON(users)
}

func (h *UserHandler) GetUser(c *fiber.Ctx) error {
    id := c.Params("id")
    user, err := h.service.GetByID(id)
    if err != nil {
        return c.Status(404).JSON(fiber.Map{
            "error": "User not found",
        })
    }
    return c.JSON(user)
}

func (h *UserHandler) CreateUser(c *fiber.Ctx) error {
    var req CreateUserRequest
    if err := c.BodyParser(&req); err != nil {
        return c.Status(400).JSON(fiber.Map{
            "error": err.Error(),
        })
    }
    user, err := h.service.Create(&req)
    if err != nil {
        return c.Status(500).JSON(fiber.Map{
            "error": err.Error(),
        })
    }
    return c.Status(201).JSON(user)
}

4.3 Service层 #

负责业务逻辑处理:

go
// internal/services/user.go
package services

import (
    "my-fiber-app/internal/models"
    "my-fiber-app/internal/repositories"
)

type UserService struct {
    repo *repositories.UserRepository
}

func NewUserService(repo *repositories.UserRepository) *UserService {
    return &UserService{repo: repo}
}

func (s *UserService) GetAll() ([]*models.User, error) {
    return s.repo.FindAll()
}

func (s *UserService) GetByID(id string) (*models.User, error) {
    return s.repo.FindByID(id)
}

func (s *UserService) Create(req *CreateUserRequest) (*models.User, error) {
    user := &models.User{
        Name:  req.Name,
        Email: req.Email,
    }
    if err := s.repo.Create(user); err != nil {
        return nil, err
    }
    return user, nil
}

4.4 Repository层 #

负责数据访问:

go
// internal/repositories/user.go
package repositories

import (
    "gorm.io/gorm"
    "my-fiber-app/internal/models"
)

type UserRepository struct {
    db *gorm.DB
}

func NewUserRepository(db *gorm.DB) *UserRepository {
    return &UserRepository{db: db}
}

func (r *UserRepository) FindAll() ([]*models.User, error) {
    var users []*models.User
    if err := r.db.Find(&users).Error; err != nil {
        return nil, err
    }
    return users, nil
}

func (r *UserRepository) FindByID(id string) (*models.User, error) {
    var user models.User
    if err := r.db.First(&user, "id = ?", id).Error; err != nil {
        return nil, err
    }
    return &user, nil
}

func (r *UserRepository) Create(user *models.User) error {
    return r.db.Create(user).Error
}

4.5 Model层 #

定义数据模型:

go
// internal/models/user.go
package models

import (
    "time"
    "github.com/google/uuid"
)

type User struct {
    ID        string    `json:"id" gorm:"primaryKey"`
    Name      string    `json:"name" gorm:"not null"`
    Email     string    `json:"email" gorm:"unique;not null"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

func (u *User) BeforeCreate(tx *gorm.DB) error {
    u.ID = uuid.New().String()
    return nil
}

五、路由组织 #

5.1 路由注册 #

go
// internal/routes/routes.go
package routes

import (
    "github.com/gofiber/fiber/v2"
    "my-fiber-app/internal/handlers"
    "my-fiber-app/internal/middleware"
)

func SetupRoutes(app *fiber.App, userHandler *handlers.UserHandler) {
    // 健康检查
    app.Get("/health", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{"status": "ok"})
    })
    
    // API v1
    v1 := app.Group("/api/v1")
    
    // 用户路由
    users := v1.Group("/users")
    users.Get("/", userHandler.GetUsers)
    users.Get("/:id", userHandler.GetUser)
    users.Post("/", userHandler.CreateUser)
    users.Put("/:id", middleware.Auth(), userHandler.UpdateUser)
    users.Delete("/:id", middleware.Auth(), userHandler.DeleteUser)
}

5.2 主入口 #

go
// cmd/api/main.go
package main

import (
    "log"
    
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/logger"
    "github.com/gofiber/fiber/v2/middleware/recover"
    
    "my-fiber-app/internal/handlers"
    "my-fiber-app/internal/repositories"
    "my-fiber-app/internal/services"
    "my-fiber-app/internal/routes"
    "my-fiber-app/pkg/config"
    "my-fiber-app/pkg/database"
)

func main() {
    // 加载配置
    cfg, err := config.Load()
    if err != nil {
        log.Fatal(err)
    }
    
    // 连接数据库
    db, err := database.Connect(cfg.Database)
    if err != nil {
        log.Fatal(err)
    }
    
    // 初始化依赖
    userRepo := repositories.NewUserRepository(db)
    userService := services.NewUserService(userRepo)
    userHandler := handlers.NewUserHandler(userService)
    
    // 创建应用
    app := fiber.New(fiber.Config{
        AppName: cfg.AppName,
    })
    
    // 全局中间件
    app.Use(logger.New())
    app.Use(recover.New())
    
    // 注册路由
    routes.SetupRoutes(app, userHandler)
    
    // 启动服务
    log.Fatal(app.Listen(":" + cfg.Port))
}

六、配置管理 #

6.1 配置文件 #

yaml
# configs/config.yaml
app:
  name: My Fiber App
  port: 3000
  env: development

database:
  host: localhost
  port: 5432
  name: myapp
  user: postgres
  password: secret

redis:
  host: localhost
  port: 6379
  db: 0

6.2 配置加载 #

go
// pkg/config/config.go
package config

import (
    "os"
    
    "github.com/spf13/viper"
)

type Config struct {
    App      AppConfig
    Database DatabaseConfig
    Redis    RedisConfig
}

type AppConfig struct {
    Name string
    Port string
    Env  string
}

type DatabaseConfig struct {
    Host     string
    Port     int
    Name     string
    User     string
    Password string
}

type RedisConfig struct {
    Host string
    Port int
    DB   int
}

func Load() (*Config, error) {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath("./configs")
    viper.AddConfigPath(".")
    
    // 环境变量覆盖
    viper.AutomaticEnv()
    
    if err := viper.ReadInConfig(); err != nil {
        return nil, err
    }
    
    var cfg Config
    if err := viper.Unmarshal(&cfg); err != nil {
        return nil, err
    }
    
    // 环境变量优先
    if port := os.Getenv("PORT"); port != "" {
        cfg.App.Port = port
    }
    
    return &cfg, nil
}

七、最佳实践 #

7.1 依赖注入 #

go
// 使用构造函数注入依赖
type UserHandler struct {
    service *services.UserService
}

func NewUserHandler(service *services.UserService) *UserHandler {
    return &UserHandler{service: service}
}

7.2 统一响应格式 #

go
// pkg/utils/response.go
package utils

import "github.com/gofiber/fiber/v2"

type Response struct {
    Code    int         `json:"code"`
    Message string      `json:"message"`
    Data    interface{} `json:"data,omitempty"`
}

func Success(c *fiber.Ctx, data interface{}) error {
    return c.JSON(Response{
        Code:    0,
        Message: "success",
        Data:    data,
    })
}

func Error(c *fiber.Ctx, code int, message string) error {
    return c.JSON(Response{
        Code:    code,
        Message: message,
    })
}

7.3 错误处理 #

go
// pkg/errors/errors.go
package errors

import "github.com/gofiber/fiber/v2"

type AppError struct {
    Code    int
    Message string
}

func (e *AppError) Error() string {
    return e.Message
}

func NewNotFoundError(message string) *AppError {
    return &AppError{Code: 404, Message: message}
}

func NewBadRequestError(message string) *AppError {
    return &AppError{Code: 400, Message: message}
}

// 全局错误处理
func ErrorHandler(c *fiber.Ctx, err error) error {
    if e, ok := err.(*AppError); ok {
        return c.Status(e.Code).JSON(fiber.Map{
            "error": e.Message,
        })
    }
    return c.Status(500).JSON(fiber.Map{
        "error": "Internal Server Error",
    })
}

八、总结 #

8.1 项目结构要点 #

要点 说明
分层架构 Handler → Service → Repository → Model
依赖注入 使用构造函数注入依赖
配置管理 配置与代码分离
错误处理 统一的错误处理机制
路由组织 按模块组织路由

8.2 下一步 #

现在你已经了解了项目结构,接下来让我们学习 路由基础,深入了解Fiber的路由系统!

最后更新:2026-03-28