互联网创业公司技术团队或多或少都会踩坑,但在工业级互联网生产方法论的经验指导下可以将创业公司的试错成本降到最低, 这是早期互联网团队技术架构搭建及选型的核心考量。本文将通过项目架构和组成、代码规约及应用环境等部分展示当下的体系构建。
不要:前人挖坑,后人遭殃
要:前人栽树,后人乘凉
一定要:不论任何程序,一定要有可debug的测试环境,并在readme中写清楚运行方式
- Web Backend boilerplate
- GRPC Server boilerplate
- Queue Consumer boilerplate
项目结构说明
后端代码规约
MySQL及缓存规约
命名规约
RESTful API 规约
后端应用环境说明
技术框架及中间件选型说明
| app 应用代码目录
| common
| model
| vo // 显示层对象,Controller层与前端相互传输对象
| dto // 数据传输对象,Service或Manager层向外传输对象
| controller // namespace统一为controller
| entity // 相当于DO,entity目录下namespace统一为entity
| global // 单例模式的全局对象,例如MySQL、Redis、Logger等对象
| model // 数据库业务层
| service // 业务层 (Thin Controller, Fat Service)
| conf // 应用配置存放目录
| dev.yaml // 开发环境
| test.yaml // 测试环境
| staging.yaml // 线上测试环境/灰度环境
| prod.yaml // 正式环境
| logs // 日志存放路径
main.go // 主程序
README.md
Controller层 - 负责鉴权、参数解析、基本参数校验、service层方法调度、VO和DTO转换、异常处理及HTTP状态返回等;
Service层 - 负责DTO接收及业务上验证、主体业务逻辑、缓存策略、调度model层方法获取数据库数据、构建返回DTO、错误Handle及向上透传等;
Model层 - 负责提供可复用的CRUD方法、错误Handle及向上透传等;
后端代码规约将作为Code Review的主要依据。规约中包含强制、建议等不同级别。
方法体上面用一句话描述方法的功能(中英文皆可)每个流程可以使用step-by-step的方法描述清楚。
package some_package
// SomeProcess receives param data, processes it and returns the result.
func SomeProcess(name string) (string, error) {
// Step 1. Do something
// Step 2. Do something
// Step 3. Ultimately we do something
return name, nil
}合理的log记录是线上排查的最后防线,但需要注意2点:
- 严禁在大批量的loop中使用Log,可能会造成巨大的日志记录负担
- 严禁将用户敏感信息如姓名、电话号码等信息记录进Log,会带来合规性风险
在满足以上两者情况下,推荐在一些复杂流程中使用唯一的request_id进行合理的进度记录,方便后续排查。
严禁在web代码中使用panic,在非Controller代码中,err != nil时将err向上透传,在Controller中进行统一处理。
反例:
var mu Sync.Mutex
func GetInstance() *singleton {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = &singleton{}
}
return instance
}请使用once.Do(),详细请参照:
https://medium.com/golang-issue/how-singleton-pattern-works-with-golang-2fdd61cd5a7f
https://refactoring.guru/design-patterns/singleton/go/example#example-1
发布前请使用 wrk 进行压测自测,除非特殊情况,否则单机至少需支持100+ QPS。
(强制) 严禁在for循环中执行SQL语句,请使用诸如GORM的 Batch Insert 的语法
for i := 0; i < 10; i++ {
db.Raw('insert into xx values (1, 1, 1)')
}表结构设计一对多、多对多
以Student entity为例,CRUD方法命名请用就使用Create,Find, Update, Delete为动词命名
package student
type StudentService interface {
CreateStudent(newStudent StudentEntity) bool
CreateStudents(newStudents []StudentEntity) bool
FindStudentById(studentId int) (StudentEntity, error)
FindStudentsInPage(page Paging) ([]StudentEntity, error)
UpdateStudentById(studentId int) bool
DeleteStudentById(studentId int) bool
}后端应用环境主要分为dev,test,staging,prod共4个环境:
| 环境 | git分支 | 数据库 | 线上服务器 | 负责人 |
|---|---|---|---|---|
| dev | develop | app_dev | dev | 所有开发 |
| test | develop | app_dev | dev | 测试 |
| staging | tag: release/v1.1.0 | app | staging | 测试 |
| prod | tag: release/v1.1.0 | app | prod | 技术负责人 |
灰度策略:按企业级别灰度?
- 日志插件 - logrus
- 配置插件 - viper
- 登录鉴权 - jwt-go
- 序列化/反序列化 - msgpack
- UT及mock - mock
- Profiling - Check here
- 限流 - RateLimit
etcd是cp, consul是ca; etcd支持grpc
缓存 - Redis
后端服务通信 - gRPC
https://github.com/Massad/gin-boilerplate https://github.com/vsouza/go-gin-boilerplate
future:
- 全局message
- 全局error code