Web项目目录结构

Web项目目录结构 #

mywebapp/
├── cmd/                  # 命令行应用入口
│   └── server/
│       └── main.go       # 主入口文件
├── internal/             # 内部包(不对外暴露)
│   ├── config/           # 配置管理
│   │   └── config.go
│   ├── handler/          # HTTP请求处理(类似Controller)
│   │   ├── user.go
│   │   └── product.go
│   ├── model/            # 数据模型定义(结构体)
│   │   ├── user.go
│   │   └── product.go
│   ├── repository/       # 数据访问层(数据库操作)
│   │   ├── user_repo.go
│   │   └── product_repo.go
│   ├── service/          # 业务逻辑层
│   │   ├── user_service.go
│   │   └── product_service.go
│   ├── middleware/       # 中间件(认证、日志等)
│   │   ├── auth.go
│   │   └── logger.go
│   └── routes/           # 路由定义
│       └── routes.go
├── pkg/                  # 公共库(可被外部引用)
│   └── utils/            # 工具函数
│       └── validator.go
├── api/                  # API文档(如Swagger)
│   └── openapi.yaml
├── web/                  # 前端相关文件
│   ├── static/           # 静态资源(CSS/JS/图片)
│   └── templates/        # HTML模板
├── migrations/           # 数据库迁移脚本
│   └── 202401010000_init.sql
├── scripts/              # 辅助脚本(部署/迁移等)
│   └── migrate_db.sh
├── test/                 # 集成/端到端测试(单元测试分散在各包)
│   └── integration/
├── go.mod                # Go模块定义
├── go.sum
└── README.md             # 项目文档

详细说明 #

  1. cmd/
    • 作用:存放项目入口文件,遵循 “一个目录对应一个可执行文件” 原则。
    • 示例cmd/server/main.go 为Web服务器入口,未来可扩展cmd/cli/处理命令行任务。
  2. internal/
    • 作用:内部代码,禁止被其他项目导入,保障封装性。
    • 子目录
      • config/:配置加载(支持环境变量、YAML/JSON文件),推荐使用 Viper 库。
      • handler/(或controller/):处理HTTP请求,解析参数、返回响应,不包含业务逻辑
      • model/:定义数据结构(如数据库表映射的Go结构体)。
      • repository/(或dao/):数据持久化操作(如数据库查询),实现 “数据库与业务解耦”
      • service/:核心业务逻辑,调用repository操作数据,供handler使用。
      • middleware/:中间件(如认证、请求日志、错误处理)。
      • routes/:集中定义路由规则,绑定handler与路径。
  3. pkg/
    • 作用:存放可供外部项目使用的公共库,如通用工具、客户端SDK等。
    • 注意:若项目无对外共享代码,可省略此目录。
  4. api/
    • 作用:存放API协议文件(如OpenAPI/Swagger规范),便于生成文档或客户端代码。
  5. web/
    • 作用:前端资源文件,支持模板渲染或静态站点。
    • 子目录
      • static/:CSS、JavaScript、图片等静态文件。
      • templates/:HTML模板(如使用Go模板引擎或Gin框架)。
  6. migrations/
    • 作用:数据库迁移脚本(使用工具如 golang-migrategoose 管理版本化DDL变更)。
  7. scripts/
    • 作用:辅助脚本(如数据库迁移、部署、代码生成),提升开发效率。
  8. 其他文件
    • go.mod/go.sum:Go模块依赖管理。
    • README.md:项目概述、环境配置、启动步骤等文档。

代码示例 #

依赖注入示例(以用户模块为例):

  1. internal/repository/user_repo.go

    package repository
    
    import "mywebapp/internal/model"
    
    type UserRepository struct {
        db *gorm.DB // 假设使用GORM
    }
    
    func NewUserRepository(db *gorm.DB) *UserRepository {
        return &UserRepository{db: db}
    }
    
    func (r *UserRepository) GetByID(id uint) (*model.User, error) {
        var user model.User
        if err := r.db.First(&user, id).Error; err != nil {
            return nil, err
        }
        return &user, nil
    }
    
  2. internal/service/user_service.go

    package service
    
    import (
        "mywebapp/internal/model"
        "mywebapp/internal/repository"
    )
    
    type UserService struct {
        repo *repository.UserRepository
    }
    
    func NewUserService(repo *repository.UserRepository) *UserService {
        return &UserService{repo: repo}
    }
    
    func (s *UserService) GetUser(id uint) (*model.User, error) {
        return s.repo.GetByID(id)
    }
    
  3. internal/handler/user.go

    package handler
    
    import (
        "net/http"
        "mywebapp/internal/service"
        "github.com/gin-gonic/gin"
    )
    
    type UserHandler struct {
        svc *service.UserService
    }
    
    func NewUserHandler(svc *service.UserService) *UserHandler {
        return &UserHandler{svc: svc}
    }
    
    func (h *UserHandler) GetUser(c *gin.Context) {
        id := c.Param("id")
        user, err := h.svc.GetUser(id)
        if err != nil {
            c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
            return
        }
        c.JSON(http.StatusOK, user)
    }
    
  4. internal/routes/routes.go

    package routes
    
    import (
        "github.com/gin-gonic/gin"
        "mywebapp/internal/handler"
        "mywebapp/internal/middleware"
    )
    
    func SetupRouter(userHandler *handler.UserHandler) *gin.Engine {
        r := gin.Default()
        r.Use(middleware.Logger()) // 应用日志中间件
    
        api := r.Group("/api")
        {
            api.GET("/users/:id", userHandler.GetUser)
        }
    
        return r
    }
    
  5. cmd/server/main.go

    package main
    
    import (
        "log"
        "mywebapp/internal/config"
        "mywebapp/internal/database"
        "mywebapp/internal/handler"
        "mywebapp/internal/repository"
        "mywebapp/internal/routes"
        "mywebapp/internal/service"
    )
    
    func main() {
        // 加载配置
        cfg, err := config.Load()
        if err != nil {
            log.Fatal("Failed to load config:", err)
        }
    
        // 初始化数据库
        db, err := database.New(cfg.DB)
        if err != nil {
            log.Fatal("Failed to connect database:", err)
        }
    
        // 依赖注入
        userRepo := repository.NewUserRepository(db)
        userSvc := service.NewUserService(userRepo)
        userHandler := handler.NewUserHandler(userSvc)
    
        // 路由设置
        r := routes.SetupRouter(userHandler)
    
        // 启动服务器
        if err := r.Run(":" + cfg.Port); err != nil {
            log.Fatal("Server failed to start:", err)
        }
    }
    

关键原则 #

  1. 分层架构:清晰划分Handler-Service-Repository层,确保职责分离。
  2. 依赖注入:通过构造函数显式传递依赖,提高可测试性和灵活性。
  3. 模块化:按功能划分目录,避免单个文件过于臃肿。
  4. 配置集中管理:统一处理环境变量、配置文件,避免硬编码。
  5. 中间件机制:统一处理跨切面逻辑(如日志、认证)。

根据项目规模灵活调整,小型项目可适当合并目录,大型项目可进一步细化(如添加pkg/errors统一错误处理)。