构建方式
最好保持传统使用make构建,避免新构建工具的构建成本
Lint
golangci-lint,并开启所有默认的linter规则
文档
doc.go存放该包的一般描述 如果一个文件放不下,那么可以准备一个docs文件夹
Readme文件
- 项目目标
- 快速入门部分:开始处理项目时应该做的事情,任何依赖项,基本示例,描述项目的外部链接
包组织方式
最推荐的方法:在不被迫添加新包的前提下,直接将整个代码保存在根目录下,避免循环依赖 创建新包的理由:
- 当你有不止一种启动应用程序的方式,例如Web API和CLI共存的时候,创建一个
/cmd
包并包含cli
和web
子包,并在运行时指定到文件go run ./cmd/cli
- 当你想提取更详细的实现时
- 当你开始为密切相关的事物添加公共前缀时
r := networkReader{}
//
r := network.Reader{}
模块化
两种组织方法
- 按种类组织
.
|-- handlers
| |-- course.go
| |-- lecture.go
| |-- profile.go
| |-- user.go
|-- main.go
|-- models
| |-- course.go
| |-- lecture.go
| |-- user.go
|-- repositories
| |-- course.go
| |-- lecture.go
| |-- user.go
|-- services
| |-- course.go
| |-- user.go
|-- utils
|-- strings.go
缺点:每个类型、常量或函数都必须是公开的,才能在项目的另一部分访问;在写代码或者debug的时候,经常需要在多个包之间寻找问题。
- 按组件组织
.
|-- course
| |-- httphandler.go
| |-- model.go
| |-- repository.go
| |-- service.go
|-- main.go
|-- profile
|-- httphandler.go
|-- model.go
|-- repository.go
|-- service.go
缺点:容易发生相互依赖的问题 例如,想在用户的个人资料上显示最近的课程,从个人资料的角度上来看,课程是一种外部依赖,解决方法是在profile下创建一个接口。
type Courses interface {
MostRecent(ctx context.Context, userID string, max int) ([]course.Model, error)
}
然后在course包中,公开实现该接口的服务
type Courses struct {
// 一些不能导出的变量
}
func (c Courses) MostRecent(ctx context.Context, userID string, max int) ([]Model, error) {
// 实现逻辑
}
最后,在main.go
中从course包中创建Courses实例并传递给profile包,便可以在不引发循环依赖的情况下实现该功能。此外,写profile的测试时,可以直接mock创建一个实现,甚至可以在没有course实现包的情况下开发和测试profile的功能。
简洁架构
应用程序或模块有4层,分别是Domain、Application、Ports、Adapters。
- Domain:业务核心,没有外部依赖,不应该知道代码是在哪个上下文执行的
- Application:应用程序用法的粘合点,所有Domain、Ports、Adapter的逻辑串联位置
- Ports: 用户输入,获取信息后传入给Application
- Adapters: 与外部通信,存储和获取数据,对低层细节的抽象。
发表回复