1.代码结构
代码分层结构是一个老生常谈的话题,好的代码结构能够使得系统易于理解、开发及维护,如果代码结构很混乱就会使得不同层级的代码块耦合,导致难以维护和拓展。
比较经典的代码结构(宏观)有Web的MVC模式分层结构,将代码分为Controller路由层、Model模型层、View视图层。
更加具体地来看,对于微服务来说(不考虑前后端一体化情况),后端只有Controller及Model层, 可以细化为:
- controller层:路由层,定义接口的路由
- service层:逻辑层,定义服务的逻辑
- dao层:数据层,定义数据库间的交互
- entity层:实体层,定义数据PO、DTO、VO等结构
- utils层:工具层,定义各类工具
2.Golang:微服务代码分层结构
这里分享一下工作中常用到的Golang微服务代码分层结构,以及每一层结构的定义及能做的事情、不能做的事情。
由于Golang是不支持包之间循环依赖的,所以从hanlders到pkgs,均为单向依赖。下面介绍各个层级的含义,以及要做的事情。
2.1.pkg
pkg包存放与业务逻辑无关的工具包,如格式化工具、结构体转换工具等。
pkg/
|- formatter/
|- formatter.go
|- converter/
|- converter.go
- converter.go
func obj2String(obj interface{}) (string, error) {
bytes, err := json.Marshal(&obj)
if err != nil {
return "", err
}
return string(bytes), nil
}
2.2.entity
entity包存放领域实体及其相关方法及枚举。
- entity包只能提供最基本的和实体相关的方法,如定义了User结构体,提供IsValidUser方法判断该User是否有效等。
- entity包不依赖于其他任何包(基础类库、pkgs包)除外,只提供最基础的领域模型定义。
entity/
|- user.go
|- item.go
- user.go
type UserType string
const (
UserTypeAdmin UserType = "admin"
UserTypeNormal UserType = "normal"
)
type User struct {
UserType UserType
UserID int64
UserName string
}
func (u *User) IsAdmin() bool {
return u.UserType == UserTypeAdmin
}
2.3.dao
dao包存放于数据库交互的所有代码,即数据的增、删、改、查。
- dao包包含领域模型的所有数据CRUD操作
- dao包不包含业务逻辑相关的操作
dao/
|- dao.go
|- user_dao.go
- dao.go
var UserDao UserDaoIF
func InitDAO() {
UserDao = new(userDao)
}
- user_dao.go
type UserDaoIF interface {
GetUser(userID int64) (*entity.User, error)
CreateUser(user *entity.User) error
}
type userDao struct{}
// CreateUser implements UserDaoIF
func (*userDao) CreateUser(user *entity.User) error {
panic("unimplemented")
}
// GetUser implements UserDaoIF
func (*userDao) GetUser(userID int64) (*entity.User, error) {
panic("unimplemented")
}
2.4.policies
policies包存放和业务逻辑校验、实体验证相关的代码。
policies/
|- policy.go
|- user_policy.go
- policy.go
var UserPolicy UserPolicyIF
func Init() {
UserPolicy = new(userPolicy)
}
- user_policy.go
type UserPolicyIF interface {
CanLogin(userID int64) bool
CanRegister(userID int64) bool
}
type userPolicy struct{}
// CanLogin implements UserPolicyIF
func (*userPolicy) CanLogin(userID int64) bool {
panic("unimplemented")
}
// CanRegister implements UserPolicyIF
func (*userPolicy) CanRegister(userID int64) bool {
panic("unimplemented")
}
2.5.services
services存放业务逻辑相关代码,是整个项目中逻辑最复杂的部分。
services/
|- service.go
|- user/
|- user_service.go
|- item/
|- item_service.go
- service.go
var UserService UserServiceIF
func Init() {
UserService = new(userService)
}
- user_service.go
type UserServiceIF interface {
UserLogin(u *entity.User) error
}
type userService struct{}
// UserLogin implements UserServiceIF
func (*userService) UserLogin(u *entity.User) error {
panic("unimplemented")
}
2.6.handlers
handlers定义了各类对外处理器入口,如http、rpc、eventbus等处理器。
- handlers中的处理器只做三件事情:接受请求解析入参、调用services完成业务逻辑、构造响应参数
- handlers不包含业务代码逻辑,应该简单地作路由使用
handlers/
|- handler.go
|- rpc/
|- user_rpc.go
|- http/
|- item_http.go
2.7.其他包
- conf包:存放相关的配置文件,如config_prod.yaml等
- script包:存放系统相关的脚本,如编译脚本build.sh等
- cmd包:存放相关的可直接运行的go脚本,如刷数脚本reflush.go等