前言
总所周知,go使用了包(package)和模块(module)来进行代码管理。基于此,形成了许多的代码组织方式的最佳实践,本文只要记录一些我了解的代码组织方式,以便在以后遇到一些新项目时,可以根据大概的目录名分辨出其大概的代码组织。
组织原则
为了符合Go的生态规范,代码组织要符合以下原则(这些原则是官方+社区形成的一些共识):
- 单一职责原则:每个包只负责一类功能
- 避免循环导入:两个包相互导入会导致编译错误,如果有交叉依赖则提取出来形成公共包
- 语义化命名:目录/包名清晰表达用途
- 测试对齐:测试文件和代码文件放在一个包内,通过加_test.go便于维护
常见的代码组织方式
1. 基础单包 / 单模块结构(适合简单工具库、小脚本)
适用于功能单一的项目(如一个简单的字符串处理库、单文件可执行程序),目录结构极简:
bash
yourproject/ # 项目根目录(模块根目录)
├── go.mod # 声明模块路径,如 module github.com/yourname/strutil
├── strutil.go # 核心代码(包名:strutil)
├── strutil_test.go # 测试代码(与源码同包)
├── helper.go # 补充代码(仍属于 strutil 包,可拆分多个文件)
└── helper_test.go # 测试代码(与源码同包)
- 特点 :所有代码属于同一个包,无需复杂目录分层,导入时直接使用模块路径(如
import "github.com/yourname/strutil"
)。 - 适用场景:单文件工具、简单的函数库(如封装几个常用工具函数)。
2. 含 internal 目录的私有结构(适合需隐藏内部实现的项目)
利用 Go 的 internal
目录特性(外部项目无法导入),将 "不希望暴露的内部逻辑" 封装,只对外暴露核心接口:
csharp
yourproject/ # 模块根目录(module github.com/yourname/payment)
├── go.mod
├── payment.go # 对外暴露的核心包(包名:payment,提供 Pay() 等接口)
├── payment_test.go
├── internal/ # 内部私有目录(外部项目无法导入)
│ ├── sign/ # 内部签名逻辑包(仅 payment 包可调用)
│ │ ├── sign.go
│ │ └── sign_test.go
│ └── utils/ # 内部工具包(仅项目内使用)
│ └── utils.go
└── api/ # 对外暴露的 API 定义包(外部可导入)
└── types.go # 定义公共数据结构(如订单结构体)
- 特点 :
internal
目录下的代码仅能被 "项目根目录及其子目录的包" 导入,避免内部实现细节被外部依赖,便于后续修改。 - 适用场景:有明确 "内外接口边界" 的项目(如支付系统、开源库),需保护核心实现逻辑。
3. 复杂项目 / 微服务结构(适合大型业务系统)
大型项目(如微服务、后端系统)会结合 "分层" 和 "功能模块" 双重维度组织,典型结构如下:
csharp
yourproject/ # 模块根目录(或多模块根目录)
├── go.mod
├── api/ # 接口定义(如 Protobuf 文件、HTTP 接口文档)
├── cmd/ # 服务入口(如用户服务、订单服务)
│ ├── user-service/
│ │ └── main.go
│ └── order-service/
│ └── main.go
├── internal/ # 内部业务逻辑
│ ├── user/ # 用户模块业务逻辑
│ │ ├── service/ # 服务层(核心业务逻辑)
│ │ ├── repo/ # 数据访问层(数据库操作)
│ │ └── model/ # 数据模型(结构体定义)
│ └── order/ # 订单模块(结构同 user)
├── pkg/ # 公共组件(如数据库连接、缓存封装)
│ ├── db/
│ └── redis/
└── configs/ # 配置文件(非代码,辅助目录)
└── app.yaml
- 特点:按 "业务模块(user/order)" 和 "分层(service/repo/model)" 组织,清晰区分核心业务与公共依赖,便于团队协作和扩展。
- 适用场景:微服务系统、大型后端项目(如电商平台、社交系统)。