golang结构化开发Web应用服务的规范指南插件goapp的使用

Golang结构化开发Web应用服务的规范指南插件goapp的使用

goapp gopher

Goapp v1.0

这是一个基于9年以上Go Web应用/服务开发经验形成的结构化开发指南,结合了领域驱动设计(DDD)和清洁架构(Clean Architecture)的理念。适用于Go 1.4+版本(引入了特殊的’internal’目录)。

目录结构

├── cmd
│   ├── server
│   │   ├── grpc
│   │   │   └── grpc.go
│   │   └── http
│   │       ├── handlers.go
│   │       ├── handlers_usernotes.go
│   │       ├── handlers_users.go
│   │       ├── http.go
│   │       └── web
│   │           └── templates
│   │               └── index.html
│   └── subscribers
│       └── kafka
│           └── kafka.go
├── docker
│   ├── docker-compose.yml
│   └── Dockerfile
├── go.mod
├── go.sum
├── internal
│   ├── api
│   │   ├── api.go
│   │   ├── usernotes.go
│   │   └── users.go
│   ├── configs
│   │   └── configs.go
│   ├── pkg
│   │   ├── apm
│   │   │   ├── apm.go
│   │   │   ├── grpc.go
│   │   │   ├── http.go
│   │   │   ├── meter.go
│   │   │   ├── prometheus.go
│   │   │   └── tracer.go
│   │   ├── logger
│   │   │   ├── default.go
│   │   │   └── logger.go
│   │   ├── postgres
│   │   │   └── postgres.go
│   │   └── sysignals
│   │       └── sysignals.go
│   ├── usernotes
│   │   ├── store_postgres.go
│   │   └── usernotes.go
│   └── users
│       ├── store_postgres.go
│       └── users.go
├── lib
│   └── goapp
│       ├── goapp.go
│       ├── go.mod
│       └── go.sum
├── LICENSE
├── main.go
├── inits.go
├── shutdown.go
├── README.md
└── schemas
    ├── functions.sql
    ├── user_notes.sql
    └── users.sql

主要组件说明

internal/configs

配置包,集中管理应用配置,便于后续从环境变量或配置中心读取配置。

// configs.go 示例
package configs

type HTTP struct {
    Host string
    Port int
}

func HTTPConfig() *HTTP {
    return &HTTP{
        Host: "0.0.0.0",
        Port: 8080,
    }
}

internal/api

API包集中管理所有对外暴露的API,确保不同I/O方式(HTTP/gRPC等)行为一致。

// api.go 示例
package api

import (
    "context"
    "github.com/naughtygopher/goapp/internal/users"
)

type API struct {
    users *users.Users
}

func New(users *users.Users) *API {
    return &API{
        users: users,
    }
}

func (a *API) CreateUser(ctx context.Context, name, email string) error {
    return a.users.Create(ctx, name, email)
}

internal/users

用户业务逻辑包,包含用户相关的核心业务逻辑。

// users.go 示例
package users

import (
    "context"
    "strings"
)

type Store interface {
    Create(ctx context.Context, name, email string) error
    Get(ctx context.Context, email string) (string, error)
}

type Users struct {
    store Store
}

func New(store Store) *Users {
    return &Users{
        store: store,
    }
}

func (u *Users) Create(ctx context.Context, name, email string) error {
    name = strings.TrimSpace(name)
    email = strings.TrimSpace(email)
    
    // 业务验证逻辑
    if name == "" {
        return errors.New("name cannot be empty")
    }
    
    return u.store.Create(ctx, name, email)
}

internal/pkg/postgres

PostgreSQL工具包,初始化数据库连接池并返回实例。

// postgres.go 示例
package postgres

import (
    "context"
    "github.com/jackc/pgx/v4/pgxpool"
)

func New(ctx context.Context, connString string) (*pgxpool.Pool, error) {
    config, err := pgxpool.ParseConfig(connString)
    if err != nil {
        return nil, err
    }
    
    return pgxpool.ConnectConfig(ctx, config)
}

完整示例Demo

// main.go 示例
package main

import (
    "context"
    "log"
    "github.com/naughtygopher/goapp/internal/api"
    "github.com/naughtygopher/goapp/internal/configs"
    "github.com/naughtygopher/goapp/internal/pkg/postgres"
    "github.com/naughtygopher/goapp/internal/users"
    "github.com/naughtygopher/goapp/cmd/server/http"
)

func main() {
    ctx := context.Background()
    
    // 初始化配置
    cfg := configs.HTTPConfig()
    
    // 初始化数据库
    db, err := postgres.New(ctx, "postgres://user:pass@localhost:5432/db")
    if err != nil {
        log.Fatal(err)
    }
    
    // 初始化用户服务
    userStore := users.NewPostgresStore(db)
    userService := users.New(userStore)
    
    // 初始化API
    appAPI := api.New(userService)
    
    // 启动HTTP服务器
    srv := http.New(cfg, appAPI)
    if err := srv.Start(); err != nil {
        log.Fatal(err)
    }
}

运行应用

$ git clone https://github.com/naughtygopher/goapp.git
$ cd goapp
# 更新internal/configs/configs.go中的数据库配置
$ TEMPLATES_BASEPATH=${PWD}/cmd/server/http/web/templates go run main.go | sed 's/\\n/\n/g;s/\\t/\t/g'

使用goapp创建新项目

$ gonew github.com/naughtygopher/goapp@latest my.app
$ cd my.app

关键设计理念

  1. 依赖注入:通过接口实现依赖反转,便于测试和维护
  2. 分层架构:清晰的业务逻辑、数据访问和API分层
  3. 错误处理:使用自定义错误包统一处理错误和日志
  4. 可观测性:集成OpenTelemetry进行应用性能监控

示例截图

APM overall

APM 1 API

Dependency flow between the layers


更多关于golang结构化开发Web应用服务的规范指南插件goapp的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang结构化开发Web应用服务的规范指南插件goapp的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 结构化 Web 应用开发规范与 goapp 插件使用指南

1. 结构化 Web 应用开发规范

1.1 项目目录结构

推荐的标准项目结构:

/myapp
├── cmd/                  # 主应用程序入口
│   └── myapp/            # 主程序目录
│       └── main.go       # 主程序入口
├── internal/             # 私有应用程序代码
│   ├── config/           # 配置处理
│   ├── controllers/      # 控制器层
│   ├── models/           # 数据模型层
│   ├── services/         # 业务逻辑层
│   ├── repositories/     # 数据访问层
│   └── middleware/       # 中间件
├── pkg/                  # 可公开的库代码
├── api/                  # API 定义文件(Swagger/Protobuf等)
├── web/                  # Web 资源(模板/静态文件)
├── scripts/              # 脚本文件
├── configs/              # 配置文件
├── tests/                # 测试代码
├── go.mod                # Go模块定义
└── go.sum                # 依赖校验

1.2 分层架构示例代码

// internal/models/user.go
package models

type User struct {
    ID       uint   `json:"id" gorm:"primaryKey"`
    Username string `json:"username" gorm:"unique"`
    Email    string `json:"email" gorm:"unique"`
}

// internal/repositories/user_repository.go
package repositories

import "myapp/internal/models"

type UserRepository interface {
    Create(user *models.User) error
    FindByID(id uint) (*models.User, error)
}

type userRepository struct {
    db *gorm.DB
}

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

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

// internal/services/user_service.go
package services

import (
    "myapp/internal/models"
    "myapp/internal/repositories"
)

type UserService interface {
    Register(user *models.User) error
    GetUser(id uint) (*models.User, error)
}

type userService struct {
    userRepo repositories.UserRepository
}

func NewUserService(userRepo repositories.UserRepository) UserService {
    return &userService{userRepo: userRepo}
}

func (s *userService) Register(user *models.User) error {
    // 业务逻辑处理
    return s.userRepo.Create(user)
}

// internal/controllers/user_controller.go
package controllers

import (
    "net/http"
    "myapp/internal/models"
    "myapp/internal/services"
)

type UserController struct {
    userService services.UserService
}

func NewUserController(userService services.UserService) *UserController {
    return &UserController{userService: userService}
}

func (c *UserController) Register(w http.ResponseWriter, r *http.Request) {
    var user models.User
    // 解析请求体...
    
    if err := c.userService.Register(&user); err != nil {
        // 错误处理
        return
    }
    
    // 成功响应
}

1.3 路由组织规范

// cmd/myapp/main.go
package main

import (
    "myapp/internal/controllers"
    "myapp/internal/middleware"
    "myapp/internal/services"
    "myapp/internal/repositories"
    
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"
)

func setupRouter(db *gorm.DB) *gin.Engine {
    r := gin.Default()
    
    // 中间件
    r.Use(middleware.Logger())
    r.Use(middleware.Recovery())
    
    // 初始化各层
    userRepo := repositories.NewUserRepository(db)
    userService := services.NewUserService(userRepo)
    userController := controllers.NewUserController(userService)
    
    // 路由分组
    api := r.Group("/api")
    {
        v1 := api.Group("/v1")
        {
            user := v1.Group("/users")
            {
                user.POST("/register", userController.Register)
                user.GET("/:id", userController.GetUser)
            }
        }
    }
    
    return r
}

2. goapp 插件使用指南

goapp 是一个帮助快速构建 Go Web 应用的插件/工具集,提供项目脚手架和常用功能集成。

2.1 安装 goapp

go install github.com/goapphub/goapp@latest

2.2 创建新项目

goapp new myapp --module github.com/yourname/myapp

2.3 项目结构生成

goapp 会生成符合最佳实践的项目结构:

/myapp
├── cmd
├── internal
│   ├── config
│   ├── controllers
│   ├── models
│   ├── repositories
│   ├── services
│   └── middleware
├── pkg
├── api
├── web
├── scripts
├── configs
├── tests
├── Dockerfile
├── Makefile
├── go.mod
└── go.sum

2.4 常用命令

# 添加新的API端点
goapp add api users -m get,post,put,delete

# 生成数据库模型
goapp generate model User username:string email:string

# 添加中间件
goapp add middleware JwtAuth

# 运行开发服务器
goapp run

# 构建生产版本
goapp build

2.5 集成功能示例

// 使用goapp的配置加载功能
import "github.com/goapphub/pkg/config"

func main() {
    // 加载配置
    cfg, err := config.Load("configs/app.yaml")
    if err != nil {
        panic(err)
    }
    
    // 初始化数据库
    db, err := initDB(cfg.Database)
    if err != nil {
        panic(err)
    }
    
    // 设置路由
    r := setupRouter(db)
    
    // 启动服务器
    r.Run(cfg.Server.Address)
}

3. 最佳实践建议

  1. 依赖注入:使用接口和构造函数进行依赖管理
  2. 错误处理:统一错误处理中间件
  3. 日志记录:结构化日志(JSON格式)
  4. 配置管理:环境变量和配置文件结合
  5. API文档:集成Swagger/OpenAPI
  6. 测试:分层测试(单元测试、集成测试)
  7. 部署:使用Docker容器化

通过遵循这些规范和利用goapp工具,可以快速构建结构清晰、易于维护的Go Web应用程序。

回到顶部