目录

Gin项目结构示例

Gin项目结构示例

最近写了一个gin项目,将整个结构简单总结了一下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
gin-el on  master
❯ tree
文件夹 PATH 列表
卷序列号为 CAF6-B649
D:.
├─common        // 通用内容数据库、jwt等
├─config        // 配置文件
├─controller    // controller请求先转成vo
├─middleware    // 中间件jwt、log等中间件
├─model         // model对应数据库表
├─repository    // repo仓储层,对数据库直接CRUD
├─routes        // 路由
├─service       // service业务逻辑实现
├─utils
│  ├─errcode    // 项目错误码
│  └─settings   // 项目配置初始化操作封装
└─vo            // view object,实现请求参数校验,后端响应的封装
client controller service repository model
请求
—> 转换成vo
—> 执行业务
—> CRUD
<==> 数据库表
<— CRUD结果
<— 业务结果
<— 封装成vo
  1. controller接收client请求,将请求内容转换成request view object,同时完成请求参数的初步校验
  2. controller将校验完成的参数传递给service执行对应的业务逻辑
  3. service通过对repository层的CRUD操作再封装和整合实现对应的业务逻辑
  4. repository实现对数据库表model的CRUD
  5. 结果原路径返回,封装成response view object返回给client,避免直接暴露model

错误模块

业务错误代码应该和Http状态码要有所区分 ,根据不同业务定义不同业务的error codeerror message,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
type ErrCode int

// user
const (
	errUser ErrCode = 2000 + iota
	ERR_USER_NAME_USED
	ERR_USER_MAIL_USED
    ...
)

var codeMsg = map[ErrCode]string{
	// user
	ERR_USER_NAME_USED:    "用户名已被占用",
	ERR_USER_MAIL_USED:    "邮箱已被占用",
    ...
}

func GetErrMsg(code ErrCode) string {
	return codeMsg[code]
}
  1. reposity仓储层出参仍然携带error
  2. service业务逻辑层才对仓储层返回error处理,转换成对应的ErrCode
  3. controller层最终返回的json数据附加上业务信息ErrCodeErrMsg

示例

User Controller层,接收、响应请求

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// User Controller
type UserController struct {
	Service service.UserService
}

func NewUserController() UserController {
	return UserController{Service: service.NewUserService()}
}

func (u UserController) Register(ctx *gin.Context) {
    // 请求转换成vo
    ...
	// 创建用户
    ...
	// 带外键信息返回
    ...
	// 发放token
    ...
    // response vo并响应
	userResponse := vo.ToUserResponse(user)
	ctx.JSON(http.StatusOK, gin.H{
		"status": code,
		"msg":    errcode.GetErrMsg(code),
		"user":   userResponse,
		"token":  token,
	})
}

User Service层,通过再封装和整合repo层的CRUD操作实现业务逻辑,但不直接操作数据库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// User Service
type UserService struct {
	UserRepo  repository.UserRepo
	GroupRepo repository.UserGroupRepo
}

func NewUserService() UserService {
	return UserService{
		UserRepo:  repository.NewUserRepo(),
		GroupRepo: repository.NewUserGroupRepo(),
	}
}

func (u UserService) Register(name, mail, password string, groupId uint) (*model.User, errcode.ErrCode) {
	// 检查用户分组
    ...
	// 用户信息唯一性检查
    ...
	// 创建用户
    ...
	// 返回用户信息
    ...
	return user, errcode.OK
}

User Repo 仓储层,直接操作数据库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// User Repo
type UserRepo struct {
	db *gorm.DB
}

func NewUserRepo() UserRepo {
	db := common.GetDB()
	db.AutoMigrate(model.User{})
	return UserRepo{db: db}
}

func (u UserRepo) Create(name, mail, password string, userGroupId uint) (*model.User, error) {
    ...
}

func (u UserRepo) Delete() error {
    ...
}

func (u UserRepo) Find(id uint) (*model.User, error) {
    ...
}

func (u UserRepo) Update() (*model.User, error) {
    ...
}

gin项目结构示例