golang实现Clean架构的服务模板插件库go-clean-template的使用

Golang实现Clean架构的服务模板插件库go-clean-template的使用

Go Clean Template Logo

概述

go-clean-template是一个基于Clean架构的Golang服务模板库,主要目的是展示如何:

  • 组织项目并防止其变成"意大利面条"代码
  • 存储业务逻辑使其保持独立、干净且可扩展
  • 在微服务增长时不失去控制

该模板遵循Robert Martin(Uncle Bob)提出的Clean Architecture原则。

快速开始

本地开发

# 启动Postgres和RabbitMQ
make compose-up
# 运行应用并执行迁移
make run

集成测试(可在CI中运行)

# 启动数据库、应用+迁移,然后运行集成测试
make compose-up-integration-test

完整Docker堆栈(含反向代理)

make compose-up-all

项目结构

cmd/app/main.go

配置和日志初始化,然后在internal/app/app.go中继续主函数。

config

采用十二要素应用配置原则,将配置存储在环境变量中。

internal/app

包含app.go文件,其中创建所有主要对象并通过"New…"构造函数进行依赖注入。

internal/controller

服务器处理层(MVC控制器),包含三种服务器实现:

  • AMQP RPC(基于RabbitMQ)
  • gRPC(基于protobuf)
  • REST API(基于Fiber框架)

internal/entity

业务逻辑实体(模型),可在任何层使用。

internal/usecase

业务逻辑层,包含:

  • internal/usecase/repo - 抽象存储仓库
  • internal/usecase/webapi - 抽象Web API

依赖注入

通过构造函数注入依赖,使业务逻辑独立于外部包:

package usecase

import (
// 无外部依赖
)

type Repository interface {
	Get()
}

type UseCase struct {
	repo Repository
}

func New(r Repository) *UseCase {
	return &UseCase{
		repo: r,
	}
}

func (uc *UseCase) Do() {
	uc.repo.Get()
}

Clean架构

关键原则

依赖反转(Dependency Inversion),依赖方向从外层指向内层,使业务逻辑和实体独立于系统其他部分。

Clean Architecture

示例交互流程

    HTTP > usecase
           usecase > repository (Postgres)
           usecase < repository (Postgres)
    HTTP < usecase

Example

完整示例

以下是一个简单的REST API实现示例:

// internal/controller/http/v1/translation.go
package v1

import (
	"net/http"

	"github.com/gofiber/fiber/v2"
	"github.com/evrone/go-clean-template/internal/entity"
	"github.com/evrone/go-clean-template/internal/usecase"
	"github.com/evrone/go-clean-template/pkg/logger"
)

type translationRoutes struct {
	t usecase.Translation
	l logger.Interface
}

func NewTranslationRoutes(handler fiber.Router, t usecase.Translation, l logger.Interface) {
	r := &translationRoutes{t, l}
	
	handler.Get("/history", r.history)
}

// @Summary     Show history
// @Description Show all translation history
// @ID          history
// @Tags  	    translation
// @Accept      json
// @Produce     json
// @Success     200 {object} []entity.Translation
// @Failure     500 {object} response
// @Router      /translation/history [get]
func (r *translationRoutes) history(c *fiber.Ctx) error {
	translations, err := r.t.History(c.Context())
	if err != nil {
		r.l.Error(err, "http - v1 - history")
		return c.Status(http.StatusInternalServerError).JSON(response{err.Error()})
	}

	return c.Status(http.StatusOK).JSON(translations)
}
// internal/usecase/translation.go
package usecase

import (
	"context"

	"github.com/evrone/go-clean-template/internal/entity"
)

type Translation interface {
	History(context.Context) ([]entity.Translation, error)
}

type TranslationUseCase struct {
	repo TranslationRepo
}

type TranslationRepo interface {
	GetHistory(context.Context) ([]entity.Translation, error)
}

func NewTranslation(r TranslationRepo) *TranslationUseCase {
	return &TranslationUseCase{r}
}

func (uc *TranslationUseCase) History(ctx context.Context) ([]entity.Translation, error) {
	return uc.repo.GetHistory(ctx)
}
// internal/entity/translation.go
package entity

type Translation struct {
	ID          int    `json:"id" db:"id"`
	Source      string `json:"source" db:"source"`
	Destination string `json:"destination" db:"destination"`
	Original    string `json:"original" db:"original"`
	Translation string `json:"translation" db:"translation"`
}

这个模板提供了清晰的架构分层和依赖管理方式,非常适合构建可维护的Golang微服务。


更多关于golang实现Clean架构的服务模板插件库go-clean-template的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现Clean架构的服务模板插件库go-clean-template的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


go-clean-template 使用指南

go-clean-template 是一个遵循 Clean 架构原则的 Golang 服务模板库,它提供了清晰的分层结构和标准化的项目组织方式。

核心概念

Clean 架构强调:

  1. 业务逻辑独立于框架、UI 和数据库
  2. 依赖关系向内指向核心业务逻辑
  3. 通过接口隔离实现细节

安装

go get github.com/evrone/go-clean-template

项目结构

典型的 go-clean-template 项目结构:

.
├── cmd/                # 应用入口
├── config/             # 配置文件
├── internal/           # 内部代码
│   ├── controller/     # 交付层(HTTP/gRPC等)
│   ├── entity/         # 业务实体
│   ├── repository/     # 数据访问接口
│   ├── usecase/        # 业务逻辑
│   └── service/        # 基础设施服务
├── pkg/                # 可复用的公共代码
└── migrations/         # 数据库迁移文件

基本使用示例

1. 定义实体

// internal/entity/user.go
package entity

type User struct {
    ID       int
    Name     string
    Email    string
    Password string
}

2. 创建仓储接口

// internal/repository/user.go
package repository

import "github.com/your-project/internal/entity"

type UserRepository interface {
    Create(user *entity.User) error
    FindByID(id int) (*entity.User, error)
    FindByEmail(email string) (*entity.User, error)
}

3. 实现业务用例

// internal/usecase/user.go
package usecase

import (
    "github.com/your-project/internal/entity"
    "github.com/your-project/internal/repository"
)

type UserUseCase struct {
    userRepo repository.UserRepository
}

func NewUserUseCase(repo repository.UserRepository) *UserUseCase {
    return &UserUseCase{userRepo: repo}
}

func (uc *UserUseCase) Register(user *entity.User) error {
    // 业务逻辑验证
    if user.Email == "" || user.Password == "" {
        return entity.ErrInvalidCredentials
    }
    
    // 检查用户是否已存在
    if _, err := uc.userRepo.FindByEmail(user.Email); err == nil {
        return entity.ErrUserAlreadyExists
    }
    
    // 创建用户
    return uc.userRepo.Create(user)
}

4. 实现控制器

// internal/controller/http/user.go
package http

import (
    "net/http"
    
    "github.com/your-project/internal/entity"
    "github.com/your-project/internal/usecase"
    "github.com/gin-gonic/gin"
)

type UserController struct {
    userUseCase *usecase.UserUseCase
}

func NewUserController(uc *usecase.UserUseCase) *UserController {
    return &UserController{userUseCase: uc}
}

func (c *UserController) Register(ctx *gin.Context) {
    var user entity.User
    if err := ctx.ShouldBindJSON(&user); err != nil {
        ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    
    if err := c.userUseCase.Register(&user); err != nil {
        ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    
    ctx.JSON(http.StatusCreated, gin.H{"message": "User created successfully"})
}

5. 依赖注入

// cmd/main.go
package main

import (
    "github.com/gin-gonic/gin"
    "github.com/your-project/internal/controller/http"
    "github.com/your-project/internal/repository/postgres"
    "github.com/your-project/internal/usecase"
)

func main() {
    // 初始化数据库连接
    db := postgres.NewPostgresDB()
    
    // 初始化仓储
    userRepo := postgres.NewUserRepository(db)
    
    // 初始化用例
    userUseCase := usecase.NewUserUseCase(userRepo)
    
    // 初始化HTTP控制器
    userController := http.NewUserController(userUseCase)
    
    // 设置路由
    r := gin.Default()
    r.POST("/register", userController.Register)
    
    // 启动服务
    r.Run(":8080")
}

高级特性

1. 中间件支持

// 添加认证中间件
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
            return
        }
        // 验证token逻辑...
        c.Next()
    }
}

// 在路由中使用
r.POST("/profile", AuthMiddleware(), userController.GetProfile)

2. 日志集成

// 初始化日志
logger := logrus.New()
logger.SetFormatter(&logrus.JSONFormatter{})

// 在用例中使用
func (uc *UserUseCase) Register(user *entity.User) error {
    logger.WithFields(logrus.Fields{
        "email": user.Email,
    }).Info("Attempting to register user")
    // ...
}

3. 配置管理

// config/config.go
type Config struct {
    DB struct {
        Host     string `env:"DB_HOST" env-default:"localhost"`
        Port     int    `env:"DB_PORT" env-default:"5432"`
        User     string `env:"DB_USER" env-default:"postgres"`
        Password string `env:"DB_PASSWORD" env-default:""`
        Name     string `env:"DB_NAME" env-default:"postgres"`
    }
    Server struct {
        Port int `env:"SERVER_PORT" env-default:"8080"`
    }
}

func Load() (*Config, error) {
    var cfg Config
    if err := env.Parse(&cfg); err != nil {
        return nil, err
    }
    return &cfg, nil
}

最佳实践

  1. 保持层间依赖清晰:上层可以依赖下层,但下层不能依赖上层
  2. 使用接口隔离:所有外部依赖都应通过接口访问
  3. 测试友好:通过依赖注入可以轻松mock外部依赖
  4. 单一职责:每个文件/结构体/函数只做一件事
  5. 领域驱动:将业务逻辑集中在用例层,保持实体纯净

go-clean-template 提供了一种结构化的方式来组织Go项目,特别适合中大型项目开发。通过遵循其约定,你可以创建出易于维护、测试和扩展的应用程序。

回到顶部