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 # 项目文档
详细说明 #
cmd/
- 作用:存放项目入口文件,遵循 “一个目录对应一个可执行文件” 原则。
- 示例:
cmd/server/main.go
为Web服务器入口,未来可扩展cmd/cli/
处理命令行任务。
internal/
- 作用:内部代码,禁止被其他项目导入,保障封装性。
- 子目录:
config/
:配置加载(支持环境变量、YAML/JSON文件),推荐使用 Viper 库。handler/
(或controller/
):处理HTTP请求,解析参数、返回响应,不包含业务逻辑。model/
:定义数据结构(如数据库表映射的Go结构体)。repository/
(或dao/
):数据持久化操作(如数据库查询),实现 “数据库与业务解耦”。service/
:核心业务逻辑,调用repository
操作数据,供handler
使用。middleware/
:中间件(如认证、请求日志、错误处理)。routes/
:集中定义路由规则,绑定handler
与路径。
pkg/
- 作用:存放可供外部项目使用的公共库,如通用工具、客户端SDK等。
- 注意:若项目无对外共享代码,可省略此目录。
api/
- 作用:存放API协议文件(如OpenAPI/Swagger规范),便于生成文档或客户端代码。
web/
- 作用:前端资源文件,支持模板渲染或静态站点。
- 子目录:
static/
:CSS、JavaScript、图片等静态文件。templates/
:HTML模板(如使用Go模板引擎或Gin框架)。
migrations/
- 作用:数据库迁移脚本(使用工具如 golang-migrate 或 goose 管理版本化DDL变更)。
scripts/
- 作用:辅助脚本(如数据库迁移、部署、代码生成),提升开发效率。
- 其他文件
go.mod/go.sum
:Go模块依赖管理。README.md
:项目概述、环境配置、启动步骤等文档。
代码示例 #
依赖注入示例(以用户模块为例):
-
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 }
-
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) }
-
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) }
-
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 }
-
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) } }
关键原则 #
- 分层架构:清晰划分Handler-Service-Repository层,确保职责分离。
- 依赖注入:通过构造函数显式传递依赖,提高可测试性和灵活性。
- 模块化:按功能划分目录,避免单个文件过于臃肿。
- 配置集中管理:统一处理环境变量、配置文件,避免硬编码。
- 中间件机制:统一处理跨切面逻辑(如日志、认证)。
根据项目规模灵活调整,小型项目可适当合并目录,大型项目可进一步细化(如添加pkg/errors
统一错误处理)。