golang项目布局示例与真实代码实践插件库go-sample的使用

Golang项目布局示例与真实代码实践插件库go-sample的使用

go-sample简介

go-sample是一个包含真实代码的Go应用程序项目示例布局,采用了多种现代架构思想:

Build Status codecov Go Report Card License

架构思想来源

该项目融合了以下架构思想:

  • 清洁架构(The Clean Architecture)
  • DDD, 六边形架构, 洋葱架构, CQRS等
  • Go应用程序中的清洁架构应用
  • 标准包布局
  • Go应用结构设计
  • 七年来编写Go HTTP服务的经验

环境要求

  • git
  • docker

安装

git clone https://github.com/zitryss/perfmon.git

使用

docker-compose up --build

示例代码结构

以下是go-sample的典型项目结构示例:

go-sample/
├── cmd/                  # 主应用程序入口
│   └── app/             # 应用主程序
│       └── main.go      # 主入口文件
├── internal/            # 私有应用程序代码
│   ├── app/             # 应用层
│   ├── domain/          # 领域层
│   ├── infrastructure/  # 基础设施层
│   └── interfaces/      # 接口层
├── pkg/                 # 可复用的公共库代码
├── configs/             # 配置文件
├── deployments/         # 部署配置
├── scripts/             # 各种脚本
├── test/                # 测试文件
├── go.mod               # Go模块定义
└── go.sum               # 依赖校验

示例代码

// cmd/app/main.go
package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/zitryss/go-sample/internal/app"
	"github.com/zitryss/go-sample/internal/infrastructure/config"
)

func main() {
	// 加载配置
	cfg, err := config.Load()
	if err != nil {
		log.Fatalf("failed to load config: %v", err)
	}

	// 创建应用
	application, err := app.New(cfg)
	if err != nil {
		log.Fatalf("failed to create app: %v", err)
	}

	// 创建上下文用于优雅关闭
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 启动应用
	go func() {
		if err := application.Run(ctx); err != nil {
			log.Printf("application stopped with error: %v", err)
			cancel()
		}
	}()

	// 等待中断信号
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit

	// 开始优雅关闭
	log.Println("shutting down application...")
	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer shutdownCancel()

	if err := application.Stop(shutdownCtx); err != nil {
		log.Printf("failed to stop application gracefully: %v", err)
	}

	log.Println("application stopped")
}
// internal/app/app.go
package app

import (
	"context"
	"fmt"
	"time"

	"github.com/zitryss/go-sample/internal/domain"
	"github.com/zitryss/go-sample/internal/infrastructure/config"
	"github.com/zitryss/go-sample/internal/interfaces"
)

type Application struct {
	cfg      *config.Config
	server   *interfaces.Server
	services *domain.Services
}

func New(cfg *config.Config) (*Application, error) {
	// 初始化领域服务
	services, err := domain.NewServices(cfg)
	if err != nil {
		return nil, fmt.Errorf("failed to create services: %w", err)
	}

	// 初始化接口层
	server, err := interfaces.NewServer(cfg, services)
	if err != nil {
		return nil, fmt.Errorf("failed to create server: %w", err)
	}

	return &Application{
		cfg:      cfg,
		server:   server,
		services: services,
	}, nil
}

func (a *Application) Run(ctx context.Context) error {
	// 启动HTTP服务器
	if err := a.server.Run(ctx); err != nil {
		return fmt.Errorf("server error: %w", err)
	}
	return nil
}

func (a *Application) Stop(ctx context.Context) error {
	// 优雅关闭服务器
	if err := a.server.Stop(ctx); err != nil {
		return fmt.Errorf("failed to stop server: %w", err)
	}
	return nil
}

这个示例展示了go-sample项目的基本结构和代码组织方式,采用了分层架构设计,包括应用层、领域层、基础设施层和接口层。


更多关于golang项目布局示例与真实代码实践插件库go-sample的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang项目布局示例与真实代码实践插件库go-sample的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang项目布局示例与真实代码实践

标准项目布局

在Go社区中,虽然没有官方的项目布局标准,但有一些被广泛接受的实践模式。以下是一个典型的Go项目结构:

go-sample/
├── cmd/               # 主应用程序目录
│   ├── app1/          # 应用1
│   │   └── main.go
│   └── app2/          # 应用2
│       └── main.go
├── internal/          # 私有应用程序和库代码
│   ├── config/        # 配置处理
│   ├── models/        # 数据模型
│   ├── handlers/      # HTTP处理器
│   └── services/      # 业务逻辑
├── pkg/               # 可被外部导入的库代码
│   ├── lib1/          # 库1
│   └── lib2/          # 库2
├── api/               # API定义文件(Swagger, protobuf等)
├── web/               # Web应用程序特定组件
├── scripts/           # 脚本执行各种构建、安装等
├── configs/           # 配置文件模板或默认配置
├── init/              # 系统初始化配置
├── build/             # 打包和持续集成
├── deployments/       # IaaS、PaaS、系统和容器编排部署配置
├── test/              # 额外的外部测试和测试数据
├── docs/              # 设计和用户文档
├── tools/             # 项目的支持工具
├── examples/          # 应用程序或公共库的示例
├── vendor/            # 依赖管理(go mod vendor)
├── go.mod             # Go模块定义
└── go.sum             # Go模块校验和

真实代码实践示例

1. 配置管理示例

// internal/config/config.go
package config

import (
	"log"
	"os"
	"sync"

	"github.com/spf13/viper"
)

type Config struct {
	Server struct {
		Port string `mapstructure:"port"`
	} `mapstructure:"server"`
	Database struct {
		DSN string `mapstructure:"dsn"`
	} `mapstructure:"database"`
}

var (
	once     sync.Once
	instance *Config
)

func Load() *Config {
	once.Do(func() {
		viper.SetConfigName("config") // 配置文件名称(无扩展名)
		viper.SetConfigType("yaml")   // 配置文件类型
		viper.AddConfigPath(".")      // 查找配置文件的路径
		viper.AddConfigPath("./configs")
		viper.AddConfigPath("/etc/appname/")
		
		// 环境变量覆盖
		viper.AutomaticEnv()
		
		if err := viper.ReadInConfig(); err != nil {
			log.Fatalf("Error reading config file, %s", err)
		}
		
		instance = &Config{}
		if err := viper.Unmarshal(instance); err != nil {
			log.Fatalf("Unable to decode into struct, %v", err)
		}
	})
	return instance
}

2. HTTP服务示例

// cmd/server/main.go
package main

import (
	"fmt"
	"net/http"
	
	"go-sample/internal/config"
	"go-sample/internal/handlers"
)

func main() {
	cfg := config.Load()
	
	mux := http.NewServeMux()
	mux.HandleFunc("/", handlers.HomeHandler)
	mux.HandleFunc("/api/users", handlers.UsersHandler)
	
	server := &http.Server{
		Addr:    ":" + cfg.Server.Port,
		Handler: mux,
	}
	
	fmt.Printf("Server starting on port %s...\n", cfg.Server.Port)
	if err := server.ListenAndServe(); err != nil {
		panic(err)
	}
}

3. 数据库层示例

// internal/models/user.go
package models

import (
	"context"
	"database/sql"
	"time"
)

type User struct {
	ID        int64     `json:"id"`
	Name      string    `json:"name"`
	Email     string    `json:"email"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

type UserRepository struct {
	db *sql.DB
}

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

func (r *UserRepository) Create(ctx context.Context, user *User) error {
	query := `
		INSERT INTO users (name, email, created_at, updated_at)
		VALUES ($1, $2, $3, $4)
		RETURNING id
	`
	
	err := r.db.QueryRowContext(ctx, query,
		user.Name,
		user.Email,
		time.Now(),
		time.Now(),
	).Scan(&user.ID)
	
	return err
}

func (r *UserRepository) GetByID(ctx context.Context, id int64) (*User, error) {
	query := `
		SELECT id, name, email, created_at, updated_at
		FROM users
		WHERE id = $1
	`
	
	user := &User{}
	err := r.db.QueryRowContext(ctx, query, id).Scan(
		&user.ID,
		&user.Name,
		&user.Email,
		&user.CreatedAt,
		&user.UpdatedAt,
	)
	
	if err != nil {
		return nil, err
	}
	
	return user, nil
}

插件库go-sample的使用

go-sample是一个常用的Go项目模板库,可以帮助快速初始化项目结构:

  1. 安装go-sample:
go get github.com/go-sample/go-sample
  1. 初始化新项目:
package main

import (
	"fmt"
	"log"
	
	"github.com/go-sample/go-sample/project"
)

func main() {
	// 创建新项目
	proj, err := project.New("my-new-project", 
		project.WithModule("github.com/username/my-new-project"),
		project.WithLayout(project.StandardLayout),
	)
	if err != nil {
		log.Fatal(err)
	}
	
	// 生成项目文件
	if err := proj.Generate(); err != nil {
		log.Fatal(err)
	}
	
	fmt.Println("Project created successfully!")
}
  1. 常用功能:
  • 生成标准项目结构
  • 添加预定义的Makefile
  • 集成CI/CD配置
  • 添加常用中间件和工具链配置

最佳实践建议

  1. 模块化设计: 将功能拆分为小的、可重用的包
  2. 依赖注入: 使用接口和构造函数管理依赖
  3. 错误处理: 始终处理错误,不要忽略它们
  4. 测试: 为关键功能编写单元测试和集成测试
  5. 文档: 为公共API添加清晰的文档注释
  6. 配置: 使用环境变量和配置文件分离配置
  7. 日志: 使用结构化的日志记录关键信息

通过遵循这些实践和项目布局,可以创建出结构清晰、易于维护的Go应用程序。

回到顶部