项目结构规范
概述
良好的项目结构是构建可维护、可扩展微服务的基础。本章将介绍 CloudWeGo 推荐的项目目录结构,帮助你组织代码、提高开发效率。
核心内容
单体项目结构
适用于小型项目或初期开发:
myapp/
├── cmd/ # 应用入口
│ └── server/
│ └── main.go
├── internal/ # 内部代码(不可导入)
│ ├── handler/ # 处理器
│ ├── service/ # 业务逻辑
│ ├── repository/ # 数据访问
│ └── model/ # 数据模型
├── pkg/ # 公共代码(可导入)
│ ├── config/ # 配置
│ ├── middleware/ # 中间件
│ └── utils/ # 工具函数
├── idl/ # IDL 定义
│ └── api.thrift
├── kitex_gen/ # 生成的代码
├── config/ # 配置文件
│ ├── config.yaml
│ └── config.test.yaml
├── scripts/ # 脚本
│ ├── build.sh
│ └── gen.sh
├── go.mod
├── go.sum
├── Makefile
└── README.md微服务项目结构
适用于中大型项目,每个服务独立仓库:
user-service/
├── cmd/
│ ├── server/ # 服务入口
│ │ └── main.go
│ └── client/ # 客户端示例
│ └── main.go
├── internal/
│ ├── handler/ # RPC 处理器
│ │ └── user_handler.go
│ ├── service/ # 业务逻辑
│ │ └── user_service.go
│ ├── repository/ # 数据访问
│ │ └── user_repo.go
│ ├── model/ # 领域模型
│ │ └── user.go
│ └── infra/ # 基础设施
│ ├── db/ # 数据库
│ ├── cache/ # 缓存
│ └── mq/ # 消息队列
├── pkg/
│ ├── config/ # 配置管理
│ │ └── config.go
│ ├── middleware/ # 中间件
│ │ ├── auth.go
│ │ └── logging.go
│ └── utils/ # 工具函数
├── idl/
│ └── user.thrift # IDL 定义
├── kitex_gen/ # 生成的代码
├── api/ # HTTP API(可选)
│ ├── handler/
│ └── router/
├── config/
│ ├── config.yaml
│ ├── config.dev.yaml
│ └── config.prod.yaml
├── scripts/
│ ├── build.sh
│ ├── gen.sh
│ └── docker.sh
├── deployments/
│ ├── docker/
│ │ └── Dockerfile
│ └── k8s/
│ └── deployment.yaml
├── docs/ # 文档
├── go.mod
├── go.sum
├── Makefile
└── README.mdMonorepo 项目结构
适用于多服务统一管理的场景:
monorepo/
├── services/ # 微服务
│ ├── user-service/
│ │ ├── cmd/
│ │ ├── internal/
│ │ └── idl/
│ ├── order-service/
│ │ ├── cmd/
│ │ ├── internal/
│ │ └── idl/
│ └── payment-service/
│ ├── cmd/
│ ├── internal/
│ └── idl/
├── api/ # 共享 IDL
│ ├── user/
│ │ └── user.thrift
│ ├── order/
│ │ └── order.thrift
│ └── common/
│ └── common.thrift
├── pkg/ # 共享代码
│ ├── middleware/
│ ├── config/
│ └── utils/
├── tools/ # 工具
│ └── gen-code.sh
├── docs/ # 文档
├── go.mod
├── go.sum
├── Makefile
└── README.md目录职责说明
cmd/
应用入口,只负责初始化和启动:
go
// cmd/server/main.go
package main
import (
"log"
"example/internal/handler"
"example/internal/service"
"example/internal/repository"
"example/pkg/config"
"github.com/cloudwego/kitex/server"
)
func main() {
// 加载配置
cfg, err := config.Load("config/config.yaml")
if err != nil {
log.Fatal(err)
}
// 初始化依赖
db := repository.NewDB(cfg.Database)
repo := repository.NewUserRepo(db)
svc := service.NewUserService(repo)
handler := handler.NewUserHandler(svc)
// 启动服务
svr := server.NewServer(handler)
if err := svr.Run(); err != nil {
log.Fatal(err)
}
}internal/
内部代码,不可被外部导入:
go
// internal/service/user_service.go
package service
type UserService struct {
repo repository.UserRepository
}
func NewUserService(repo repository.UserRepository) *UserService {
return &UserService{repo: repo}
}
func (s *UserService) GetUser(ctx context.Context, id int64) (*model.User, error) {
return s.repo.FindByID(ctx, id)
}pkg/
公共代码,可被外部导入:
go
// pkg/middleware/auth.go
package middleware
import (
"context"
"github.com/cloudwego/kitex/pkg/klog"
)
func AuthMiddleware(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
// 认证逻辑
klog.CtxInfof(ctx, "auth middleware")
return next(ctx, req)
}
}idl/
IDL 定义文件:
thrift
// idl/user.thrift
namespace go user
include "common.thrift"
struct User {
1: i64 id
2: string name
3: string email
}
service UserService {
User GetUser(1: i64 id)
}配置管理
配置文件结构
yaml
# config/config.yaml
server:
name: user-service
port: 8888
database:
driver: mysql
dsn: "user:pass@tcp(localhost:3306)/db?charset=utf8mb4"
max_open: 100
max_idle: 10
redis:
addr: "localhost:6379"
password: ""
db: 0
log:
level: info
output: stdout
registry:
type: nacos
address: "localhost:8848"
namespace: "dev"配置加载
go
// pkg/config/config.go
package config
import (
"os"
"gopkg.in/yaml.v3"
)
type Config struct {
Server ServerConfig `yaml:"server"`
Database DatabaseConfig `yaml:"database"`
Redis RedisConfig `yaml:"redis"`
Log LogConfig `yaml:"log"`
Registry RegistryConfig `yaml:"registry"`
}
func Load(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, err
}
return &cfg, nil
}Makefile 示例
makefile
.PHONY: all build run gen clean
APP_NAME := user-service
MAIN_FILE := cmd/server/main.go
all: gen build
build:
go build -o bin/$(APP_NAME) $(MAIN_FILE)
run:
go run $(MAIN_FILE)
gen:
kitex -module example -service user idl/user.thrift
clean:
rm -rf kitex_gen bin
test:
go test -v ./...
docker:
docker build -t $(APP_NAME) -f deployments/docker/Dockerfile .Dockerfile 示例
dockerfile
# deployments/docker/Dockerfile
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o bin/server ./cmd/server
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/bin/server .
COPY --from=builder /app/config ./config
EXPOSE 8888
CMD ["./server"]大型项目组织最佳实践
1. 模块化设计
大型项目的模块划分:
user-service/
├── cmd/ # 应用入口
├── internal/ # 内部代码
│ ├── modules/ # 业务模块
│ │ ├── user/ # 用户模块
│ │ │ ├── handler/ # 处理器
│ │ │ ├── service/ # 业务逻辑
│ │ │ ├── repository/ # 数据访问
│ │ │ └── model/ # 数据模型
│ │ └── auth/ # 认证模块
│ └── infra/ # 基础设施
├── pkg/ # 公共代码
├── idl/ # IDL 定义
└── config/ # 配置文件模块设计原则:
- 高内聚:每个模块职责单一
- 低耦合:模块间依赖最小化
- 可测试:模块可独立测试
- 可扩展:支持功能扩展
2. 依赖管理
Go 模块依赖管理:
go
// go.mod
module example.com/user-service
go 1.21
require (
github.com/cloudwego/kitex v0.10.0
github.com/cloudwego/hertz v0.7.0
gopkg.in/yaml.v3 v3.0.1
)
require (
// 间接依赖
)依赖管理最佳实践:
- 使用 Go 1.18+ 的模块系统
- 锁定依赖版本(使用 go.sum)
- 定期更新依赖
- 使用依赖分析工具(如 go mod why)
- 避免循环依赖
3. 代码组织
分层架构:
| 层级 | 职责 | 示例目录 |
|---|---|---|
| 接口层 | 处理请求和响应 | internal/handler |
| 业务逻辑层 | 核心业务逻辑 | internal/service |
| 领域模型层 | 业务实体 | internal/model |
| 数据访问层 | 数据库操作 | internal/repository |
| 基础设施层 | 外部依赖 | internal/infra |
代码组织原则:
- 按功能模块组织代码
- 遵循单一职责原则
- 使用依赖注入
- 接口与实现分离
4. 配置管理
大型项目的配置管理:
config/
├── default.yaml # 默认配置
├── development.yaml # 开发环境
├── staging.yaml # 测试环境
├── production.yaml # 生产环境
└── secrets/ # 敏感配置配置管理最佳实践:
- 使用分层配置(默认 + 环境)
- 敏感配置使用环境变量或密钥管理服务
- 配置变更记录和审计
- 配置验证和类型安全
- 配置热加载
配置加载示例:
go
// pkg/config/config.go
package config
import (
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
type Config struct {
Server ServerConfig `yaml:"server"`
Database DatabaseConfig `yaml:"database"`
Redis RedisConfig `yaml:"redis"`
}
func Load() (*Config, error) {
// 加载默认配置
defaultPath := "config/default.yaml"
// 加载环境配置
env := os.Getenv("APP_ENV")
if env == "" {
env = "development"
}
envPath := filepath.Join("config", env+".yaml")
// 合并配置
cfg, err := loadFile(defaultPath)
if err != nil {
return nil, err
}
envCfg, err := loadFile(envPath)
if err != nil {
return nil, err
}
// 合并环境配置到默认配置
mergeConfig(cfg, envCfg)
return cfg, nil
}5. 构建和部署
大型项目的构建策略:
makefile
# Makefile
.PHONY: all build test deploy
APP_NAME := user-service
all: build
build:
@echo "Building $(APP_NAME)..."
@mkdir -p bin
@go build -o bin/$(APP_NAME) ./cmd/server
test:
@echo "Running tests..."
@go test -v ./...
docker:
@echo "Building Docker image..."
@docker build -t $(APP_NAME) -f deployments/docker/Dockerfile .
deploy:
@echo "Deploying to Kubernetes..."
@kubectl apply -f deployments/k8s/deployment.yaml
@kubectl apply -f deployments/k8s/service.yaml
helm:
@echo "Deploying with Helm..."
@helm install $(APP_NAME) deployments/helm部署策略:
- 容器化:使用 Docker 容器
- 编排:使用 Kubernetes
- 包管理:使用 Helm
- 滚动更新:无 downtime 部署
- 健康检查:配置就绪和存活探针
6. 测试策略
大型项目的测试分层:
| 测试类型 | 目的 | 位置 | 执行频率 |
|---|---|---|---|
| 单元测试 | 测试单个函数 | *test.go 文件 | 每次代码变更 |
| 集成测试 | 测试模块间交互 | internal/integration | 每日构建 |
| 端到端测试 | 测试完整流程 | test/e2e | 发布前 |
| 性能测试 | 测试性能指标 | test/performance | 版本发布 |
测试最佳实践:
- 使用表驱动测试
- 模拟外部依赖
- 测试覆盖率目标(如 80%+)
- 测试自动化
- 测试环境隔离
7. CI/CD 流程
大型项目的 CI/CD 流程:
yaml
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '1.21'
- name: Test
run: go test -v ./...
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: make build
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Deploy to staging
run: make deployCI/CD 最佳实践:
- 自动化测试
- 代码质量检查
- 安全扫描
- 环境隔离
- 部署自动化
- 回滚机制
8. 监控和可观测性
大型项目的监控:
monitoring/
├── prometheus/ # Prometheus 配置
├── grafana/ # Grafana 仪表板
└── alertmanager/ # 告警配置可观测性最佳实践:
- 指标:使用 Prometheus 收集指标
- 追踪:使用 OpenTelemetry 进行分布式追踪
- 日志:结构化日志和集中式日志管理
- 告警:基于阈值的告警
- 仪表板:实时监控系统状态
小结
本章介绍了 CloudWeGo 推荐的项目结构和大型项目组织最佳实践:
项目结构:
- 单体项目:适用于小型项目
- 微服务项目:适用于中大型项目
- Monorepo 项目:适用于多服务统一管理
目录职责:
- cmd:应用入口
- internal:内部代码
- pkg:公共代码
- idl:IDL 定义
- config:配置文件
大型项目最佳实践:
- 模块化设计:高内聚、低耦合
- 依赖管理:版本锁定、定期更新
- 代码组织:分层架构、单一职责
- 配置管理:分层配置、敏感信息处理
- 构建部署:容器化、编排、滚动更新
- 测试策略:分层测试、自动化测试
- CI/CD 流程:自动化、质量检查、安全扫描
- 监控可观测性:指标、追踪、日志、告警
工具支持:
- Makefile:构建和管理
- Dockerfile:容器化
- Kubernetes:编排和部署
- Helm:包管理
通过本章的学习,你应该掌握了如何组织和管理 CloudWeGo 项目,从小型项目到大型微服务架构,都能找到合适的项目结构和组织方式。在下一章中,我们将深入学习 Kitex RPC 框架的使用。