Golang中组织简单Web API服务器的最佳实践

Golang中组织简单Web API服务器的最佳实践 作为一名开发者,我大部分经验是使用C#构建CRUD Web API服务器。我通常采用模型-视图-控制器(MVC)结构来组织它们,并且经常为MySQL查询添加一个Repository层。

在学习Go语言的过程中,我了解到在Go中并不推荐使用MVC模式(链接)。相反,我应该使用“面向包的设计”。然而,我觉得我找到的示例都是为比我想要开始构建的应用程序更大型的应用而设计的。

对于简单的服务器,你们采用什么样的设计?有没有一种你反复使用的模式?


更多关于Golang中组织简单Web API服务器的最佳实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

我发现了Kat Zien的这个演讲:如何构建你的Go应用

更多关于Golang中组织简单Web API服务器的最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


看看这段代码:https://github.com/santosh/bookstore-api/blob/master/main.go

它会解答你的一些疑问。我也正在学习 Go 语言,你觉得它怎么样?

这是我第一次开发API服务器(个人项目),目前我正在遵循Ben Johnson讨论的标准包布局方法。

嗯,我也是为了同样的问题而来……希望有人能在这里发布一个准确的解决方案。

在我看来,MVC模式对于“简单”的服务器来说效果不错。然而,你实现它的方式很重要。例如,将你的包命名为“controllers”、“models”等,将会使其难以维护和扩展。根据我的经验,你将面临的最常见问题是避免导入循环,因为Go语言不允许循环导入。即使你最终以某种方式避免了这个问题,我认为确保每个控制器/模型都有不同的名称也是一件令人疲惫的事情。请查看这个。建议的方法确实避免了这些基本问题,并且在某种程度上,即使不是完全,也是面向包的。请随意浏览论坛的其余部分,其中讨论了许多设计模式及其优缺点,你可能会觉得有用。

希望这能帮到你!

在Go中组织简单Web API服务器,我推荐采用分层结构(handler-service-repository),这种模式在实践中被广泛采用且易于维护。以下是一个典型的结构示例:

// 项目结构
project/
├── cmd/
│   └── api/
│       └── main.go
├── internal/
│   ├── handler/
│   │   └── user.go
│   ├── service/
│   │   └── user.go
│   ├── repository/
│   │   └── user.go
│   └── model/
│       └── user.go
└── go.mod

1. 模型层(model)

// internal/model/user.go
package model

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

2. 仓储层(repository)

// internal/repository/user.go
package repository

import "yourproject/internal/model"

type UserRepository interface {
    FindByID(id int) (*model.User, error)
    Save(user *model.User) error
}

type mysqlUserRepository struct {
    db *sql.DB
}

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

func (r *mysqlUserRepository) FindByID(id int) (*model.User, error) {
    // MySQL查询实现
    row := r.db.QueryRow("SELECT id, name, email FROM users WHERE id = ?", id)
    user := &model.User{}
    err := row.Scan(&user.ID, &user.Name, &user.Email)
    return user, err
}

3. 服务层(service)

// internal/service/user.go
package service

import (
    "yourproject/internal/model"
    "yourproject/internal/repository"
)

type UserService struct {
    repo repository.UserRepository
}

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

func (s *UserService) GetUser(id int) (*model.User, error) {
    // 业务逻辑
    return s.repo.FindByID(id)
}

4. 处理器层(handler)

// internal/handler/user.go
package handler

import (
    "encoding/json"
    "net/http"
    "strconv"
    "yourproject/internal/service"
)

type UserHandler struct {
    service *service.UserService
}

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

func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
    idStr := r.URL.Query().Get("id")
    id, _ := strconv.Atoi(idStr)
    
    user, err := h.service.GetUser(id)
    if err != nil {
        http.Error(w, err.Error(), http.StatusNotFound)
        return
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

5. 主程序入口

// cmd/api/main.go
package main

import (
    "database/sql"
    "log"
    "net/http"
    "yourproject/internal/handler"
    "yourproject/internal/repository"
    "yourproject/internal/service"
    
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // 初始化数据库连接
    db, err := sql.Open("mysql", "user:pass@/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    
    // 依赖注入
    userRepo := repository.NewUserRepository(db)
    userService := service.NewUserService(userRepo)
    userHandler := handler.NewUserHandler(userService)
    
    // 路由设置
    http.HandleFunc("/user", userHandler.GetUser)
    
    // 启动服务器
    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

这种分层结构提供了清晰的关注点分离:

  • Handler:处理HTTP请求/响应,输入验证
  • Service:包含业务逻辑
  • Repository:处理数据持久化
  • Model:定义数据结构

对于更简单的场景,可以合并service和repository层,但对于大多数CRUD API服务器,这种三层结构在可维护性和可测试性之间取得了良好平衡。每个包都有明确的职责,通过接口实现依赖倒置,便于单元测试和后续扩展。

回到顶部