应用结构 #
一、项目结构概述 #
良好的项目结构是可维护、可扩展应用的基础。
1.1 结构设计原则 #
text
┌─────────────────────────────────────────────────────────┐
│ 项目结构设计原则 │
├─────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 清晰分层│ │ 职责单一│ │ 易于测试│ │ 易于扩展│ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘
二、简单项目结构 #
2.1 目录结构 #
text
simple-app/
├── main.go
├── handlers/
│ └── user.go
├── models/
│ └── user.go
├── go.mod
└── go.sum
2.2 代码示例 #
main.go
go
package main
import (
"simple-app/handlers"
"github.com/labstack/echo/v4"
)
func main() {
e := echo.New()
e.GET("/users", handlers.GetUsers)
e.GET("/users/:id", handlers.GetUser)
e.POST("/users", handlers.CreateUser)
e.Start(":8080")
}
handlers/user.go
go
package handlers
import (
"net/http"
"simple-app/models"
"github.com/labstack/echo/v4"
)
func GetUsers(c echo.Context) error {
users := models.GetAllUsers()
return c.JSON(http.StatusOK, users)
}
func GetUser(c echo.Context) error {
id := c.Param("id")
user := models.GetUserByID(id)
return c.JSON(http.StatusOK, user)
}
func CreateUser(c echo.Context) error {
u := new(models.User)
if err := c.Bind(u); err != nil {
return err
}
models.CreateUser(u)
return c.JSON(http.StatusCreated, u)
}
models/user.go
go
package models
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
var users = []User{
{ID: 1, Name: "张三"},
{ID: 2, Name: "李四"},
}
func GetAllUsers() []User {
return users
}
func GetUserByID(id string) *User {
for _, u := range users {
if string(rune(u.ID)) == id {
return &u
}
}
return nil
}
func CreateUser(u *User) {
u.ID = len(users) + 1
users = append(users, *u)
}
三、中型项目结构 #
3.1 目录结构 #
text
medium-app/
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── handlers/
│ │ ├── handler.go
│ │ ├── user.go
│ │ └── auth.go
│ ├── models/
│ │ └── user.go
│ ├── services/
│ │ ├── user.go
│ │ └── auth.go
│ ├── repositories/
│ │ └── user.go
│ └── config/
│ └── config.go
├── pkg/
│ ├── response/
│ │ └── response.go
│ └── middleware/
│ └── auth.go
├── config/
│ └── config.yaml
├── go.mod
└── go.sum
3.2 分层架构 #
text
┌─────────────────────────────────────────────┐
│ HTTP Request │
└─────────────────┬───────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Handler Layer (处理层) │
│ 接收请求、参数验证、返回响应 │
└─────────────────┬───────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Service Layer (业务层) │
│ 业务逻辑、数据处理 │
└─────────────────┬───────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Repository Layer (数据访问层) │
│ 数据库操作、缓存操作 │
└─────────────────┬───────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Database/Cache │
└─────────────────────────────────────────────┘
3.3 代码实现 #
cmd/server/main.go
go
package main
import (
"medium-app/internal/config"
"medium-app/internal/handlers"
"medium-app/pkg/middleware"
"github.com/labstack/echo/v4"
)
func main() {
cfg := config.Load()
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
h := handlers.NewHandler(cfg)
api := e.Group("/api")
{
api.GET("/users", h.GetUsers)
api.GET("/users/:id", h.GetUser)
api.POST("/users", h.CreateUser)
}
e.Start(":" + cfg.Port)
}
internal/config/config.go
go
package config
import (
"github.com/spf13/viper"
)
type Config struct {
Port string
Database DatabaseConfig
Redis RedisConfig
}
type DatabaseConfig struct {
Host string
Port int
User string
Password string
Name string
}
type RedisConfig struct {
Host string
Port int
DB int
}
func Load() *Config {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("./config")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
return &Config{
Port: viper.GetString("app.port"),
Database: DatabaseConfig{
Host: viper.GetString("database.host"),
Port: viper.GetInt("database.port"),
User: viper.GetString("database.user"),
Password: viper.GetString("database.password"),
Name: viper.GetString("database.name"),
},
Redis: RedisConfig{
Host: viper.GetString("redis.host"),
Port: viper.GetInt("redis.port"),
DB: viper.GetInt("redis.db"),
},
}
}
internal/handlers/handler.go
go
package handlers
import (
"medium-app/internal/config"
"medium-app/internal/services"
)
type Handler struct {
UserService *services.UserService
AuthService *services.AuthService
}
func NewHandler(cfg *config.Config) *Handler {
return &Handler{
UserService: services.NewUserService(cfg),
AuthService: services.NewAuthService(cfg),
}
}
internal/handlers/user.go
go
package handlers
import (
"net/http"
"medium-app/internal/models"
"medium-app/pkg/response"
"github.com/labstack/echo/v4"
)
func (h *Handler) GetUsers(c echo.Context) error {
users, err := h.UserService.GetAll()
if err != nil {
return response.Error(c, http.StatusInternalServerError, err.Error())
}
return response.Success(c, users)
}
func (h *Handler) GetUser(c echo.Context) error {
id := c.Param("id")
user, err := h.UserService.GetByID(id)
if err != nil {
return response.Error(c, http.StatusNotFound, "用户不存在")
}
return response.Success(c, user)
}
func (h *Handler) CreateUser(c echo.Context) error {
u := new(models.User)
if err := c.Bind(u); err != nil {
return response.Error(c, http.StatusBadRequest, "参数错误")
}
if err := h.UserService.Create(u); err != nil {
return response.Error(c, http.StatusInternalServerError, err.Error())
}
return response.Success(c, u)
}
internal/services/user.go
go
package services
import (
"medium-app/internal/config"
"medium-app/internal/models"
"medium-app/internal/repositories"
)
type UserService struct {
repo *repositories.UserRepository
}
func NewUserService(cfg *config.Config) *UserService {
return &UserService{
repo: repositories.NewUserRepository(cfg),
}
}
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(u *models.User) error {
return s.repo.Save(u)
}
internal/repositories/user.go
go
package repositories
import (
"medium-app/internal/config"
"medium-app/internal/models"
)
type UserRepository struct {
db *config.DatabaseConfig
}
func NewUserRepository(cfg *config.Config) *UserRepository {
return &UserRepository{
db: &cfg.Database,
}
}
func (r *UserRepository) FindAll() ([]models.User, error) {
return []models.User{
{ID: 1, Name: "张三"},
{ID: 2, Name: "李四"},
}, nil
}
func (r *UserRepository) FindByID(id string) (*models.User, error) {
return &models.User{ID: 1, Name: "张三"}, nil
}
func (r *UserRepository) Save(u *models.User) error {
return nil
}
pkg/response/response.go
go
package response
import (
"net/http"
"github.com/labstack/echo/v4"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
func Success(c echo.Context, data interface{}) error {
return c.JSON(http.StatusOK, Response{
Code: 0,
Message: "success",
Data: data,
})
}
func Error(c echo.Context, code int, message string) error {
return c.JSON(code, Response{
Code: code,
Message: message,
})
}
四、企业级项目结构 #
4.1 目录结构 #
text
enterprise-app/
├── api/
│ └── openapi.yaml
├── cmd/
│ ├── server/
│ │ └── main.go
│ └── cli/
│ └── main.go
├── internal/
│ ├── domain/
│ │ ├── user/
│ │ │ ├── entity.go
│ │ │ ├── repository.go
│ │ │ └── service.go
│ │ └── auth/
│ │ ├── entity.go
│ │ ├── repository.go
│ │ └── service.go
│ ├── infrastructure/
│ │ ├── database/
│ │ │ ├── mysql.go
│ │ │ └── redis.go
│ │ └── logger/
│ │ └── logger.go
│ ├── interfaces/
│ │ ├── http/
│ │ │ ├── handler/
│ │ │ │ ├── user.go
│ │ │ │ └── auth.go
│ │ │ ├── middleware/
│ │ │ │ ├── auth.go
│ │ │ │ └── logging.go
│ │ │ └── router/
│ │ │ └── router.go
│ │ └── grpc/
│ │ └── user.go
│ └── app/
│ └── container.go
├── pkg/
│ ├── errors/
│ │ └── errors.go
│ ├── validator/
│ │ └── validator.go
│ └── utils/
│ └── utils.go
├── config/
│ ├── config.yaml
│ └── config.prod.yaml
├── deployments/
│ ├── docker/
│ │ ├── Dockerfile
│ │ └── docker-compose.yaml
│ └── k8s/
│ ├── deployment.yaml
│ └── service.yaml
├── scripts/
│ ├── build.sh
│ └── deploy.sh
├── docs/
│ └── swagger.yaml
├── go.mod
└── go.sum
4.2 领域驱动设计 #
text
┌─────────────────────────────────────────────────────────┐
│ Interfaces Layer │
│ HTTP Handler / gRPC Service / CLI │
├─────────────────────────────────────────────────────────┤
│ Application Layer │
│ Use Cases / Application Services │
├─────────────────────────────────────────────────────────┤
│ Domain Layer │
│ Entities / Value Objects / Domain Services │
├─────────────────────────────────────────────────────────┤
│ Infrastructure Layer │
│ Database / Cache / Message Queue / Logger │
└─────────────────────────────────────────────────────────┘
五、路由组织 #
5.1 路由分组 #
go
func setupRoutes(e *echo.Echo, h *Handler) {
api := e.Group("/api/v1")
api.GET("/health", h.Health)
users := api.Group("/users")
{
users.GET("", h.GetUsers)
users.GET("/:id", h.GetUser)
users.POST("", h.CreateUser)
users.PUT("/:id", h.UpdateUser)
users.DELETE("/:id", h.DeleteUser)
}
auth := api.Group("/auth")
{
auth.POST("/login", h.Login)
auth.POST("/register", h.Register)
auth.POST("/logout", h.Logout)
}
protected := api.Group("")
protected.Use(middleware.JWT([]byte("secret")))
{
protected.GET("/profile", h.GetProfile)
protected.PUT("/profile", h.UpdateProfile)
}
}
5.2 路由文件分离 #
internal/interfaces/http/router/router.go
go
package router
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func Setup(e *echo.Echo, handlers *Handlers) {
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.CORS())
setupUserRoutes(e, handlers.User)
setupAuthRoutes(e, handlers.Auth)
}
func setupUserRoutes(e *echo.Echo, h *UserHandler) {
g := e.Group("/api/v1/users")
g.GET("", h.List)
g.GET("/:id", h.Get)
g.POST("", h.Create)
g.PUT("/:id", h.Update)
g.DELETE("/:id", h.Delete)
}
func setupAuthRoutes(e *echo.Echo, h *AuthHandler) {
g := e.Group("/api/v1/auth")
g.POST("/login", h.Login)
g.POST("/register", h.Register)
}
六、依赖注入 #
6.1 手动注入 #
go
func main() {
db := database.New()
redis := cache.New()
userRepo := repositories.NewUserRepository(db)
userSvc := services.NewUserService(userRepo, redis)
userHandler := handlers.NewUserHandler(userSvc)
e := echo.New()
e.GET("/users", userHandler.List)
e.Start(":8080")
}
6.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/labstack/echo/v4"
)
func InitializeApp() *echo.Echo {
wire.Build(
database.New,
cache.New,
repositories.NewUserRepository,
services.NewUserService,
handlers.NewUserHandler,
NewEcho,
)
return nil
}
func NewEcho(h *handlers.UserHandler) *echo.Echo {
e := echo.New()
e.GET("/users", h.List)
return e
}
生成代码:
bash
wire
七、配置管理 #
7.1 多环境配置 #
text
config/
├── config.yaml
├── config.dev.yaml
├── config.test.yaml
└── config.prod.yaml
7.2 配置加载 #
go
func LoadConfig() *Config {
env := os.Getenv("APP_ENV")
if env == "" {
env = "dev"
}
viper.SetConfigName("config." + env)
viper.SetConfigType("yaml")
viper.AddConfigPath("./config")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
panic(err)
}
return &cfg
}
八、最佳实践 #
8.1 目录命名规范 #
| 目录 | 用途 |
|---|---|
| cmd | 程序入口 |
| internal | 私有代码 |
| pkg | 公共代码 |
| api | API定义 |
| config | 配置文件 |
| deployments | 部署配置 |
| docs | 文档 |
| scripts | 脚本 |
8.2 文件命名规范 #
| 文件 | 用途 |
|---|---|
| main.go | 程序入口 |
| handler.go | HTTP处理 |
| service.go | 业务逻辑 |
| repository.go | 数据访问 |
| model.go | 数据模型 |
| middleware.go | 中间件 |
| router.go | 路由配置 |
8.3 代码组织建议 #
- 单一职责:每个文件只做一件事
- 依赖注入:通过参数传递依赖
- 接口抽象:面向接口编程
- 错误处理:统一错误处理
- 日志规范:统一日志格式
九、总结 #
项目结构要点:
| 结构 | 适用场景 |
|---|---|
| 简单结构 | 小项目、学习项目 |
| 中型结构 | 中等项目、团队协作 |
| 企业结构 | 大型项目、微服务 |
关键原则:
- 分层清晰:Handler → Service → Repository
- 职责单一:每个模块只负责一件事
- 依赖注入:便于测试和替换
- 配置管理:支持多环境
准备好学习路由系统了吗?让我们进入下一章!
最后更新:2026-03-28