聊聊Go 应用程序设计标准

开发 前端
本文旨在建议团队制定成员都要遵循的规则,作为团队的 Go 应用程序设计标准,而不是建议大家必须遵循本文介绍的 4 个规则。

1.介绍

众所周知 Go 语言官方成员 Russ Cox 曾向 Go 社区回应并没有 Go 应用程序设计标准。但是,为什么本文还要使用这个标题呢?

因为团队达成一个共识(标准),制定一些团队成员都要遵循的规则,可以使我们的应用程序更容易维护。本文介绍一下我们应该怎么组织我们的代码,制定团队的 Go 应用程序设计标准。

需要注意的是,它不是核心 Go 开发团队制定的官方标准。

2.定义 domain 包

为什么需要定义 domain 包?因为我们开发的 Go 应用程序,可能不只是包含一个功能模块,并且可能不同的功能模块之间还需要互相调用,所以,我们需要 domain(领域)包,例如我们开发一个博客应用程序,我们的 domain 包括用户、文章、评论等。这些不依赖我们使用的底层技术。

需要注意的是,domain 包不应该包含方法的实现细节,比如操作数据库或调用其他微服务,并且 domain 包不可以依赖应用程序中的其他包。

我们可以定义 domain 包,把结构体和接口放在 domain 包,例如:

package domain

import "context"

type User struct {
Id int64 `json:"id"`
UserName string `json:"user_name" xorm:"varchar(30) notnull default '' unique comment('用户名')"`
Email string `json:"email" xorm:"varchar(30) not null default '' index comment('邮箱')"`
Password string `json:"password" xorm:"varchar(60) not null default '' comment('密码')"`
Created int `json:"created" xorm:"index created"`
Updated int `json:"updated" xorm:"updated"`
Deleted int `json:"deleted" xorm:"deleted"`
}

type UserUsecase interface {
GetById(ctx context.Context, id int) (*User, error)
GetByPage(ctx context.Context, count, offset int) ([]*User, int, error)
Create(ctx context.Context, user *User) error
Delete(ctx context.Context, id int) error
Update(ctx context.Context, user *User) error
}

type UserRepository interface {
GetById(ctx context.Context, id int) (*User, error)
GetByPage(ctx context.Context, count, offset int) ([]*User, int, error)
Create(ctx context.Context, user *User) error
Delete(ctx context.Context, id int) error
Update(ctx context.Context, user *User) error
}

细心的读者朋友们可能已经发现,以上代码在「Go 语言整洁架构实践」一文中,它是被划分到 models 包。是的,因为当时我们的示例项目是 TodoList,它仅包含一个功能模块。

但是,当我们开发一个包含多个功能模块的应用程序时,为了方便功能模块之间相互调用,更建议将所有功能模块的结构体和接口存放到 domain 包。

3.按照依赖关系划分包

在「Go 语言整洁架构实践」一文中,提到在 Repository 层存放操作数据库和调用微服务的代码,我们可以在 Repository 层按照依赖关系划分包,比如我们的应用程序需要操作 MySQL 数据库,我们可以定义一个 mysql 包。

示例代码:

package mysql

import (
"context"
"go_standard/domain"
"xorm.io/xorm"
)

type mysqlUserRepository struct {
Conn *xorm.Engine
}

func NewMysqlUserRepository(Conn *xorm.Engine) domain.UserRepository {
_ = Conn.Sync2(new(domain.User))
return &mysqlUserRepository{Conn}
}

func (m *mysqlUserRepository) GetById(ctx context.Context, id int) (res *domain.User, err error) {
// TODO::implements it
return
}

func (m *mysqlUserRepository) GetByPage(ctx context.Context, count, offset int) (data []*domain.User, nextOffset int, err error) {
// TODO::implements it
return
}

func (m *mysqlUserRepository) Create(ctx context.Context, user *domain.User) (err error) {
// TODO::implements it
return
}

func (m *mysqlUserRepository) Delete(ctx context.Context, id int) (err error) {
// TODO::implements it
return
}

func (m *mysqlUserRepository) Update(ctx context.Context, user *domain.User) (err error) {
// TODO::implements it
return
}

阅读上面这段代码,我们可以发现 mysql 包主要作为 domain 包和操作数据库的方法实现之间的适配器,这种包布局方式,隔离了我们 MySQL 的依赖关系,从而方便了未来迁移到其他数据库的实现。比如,我们未来想把数据库切换为 PostgreSQL,我们可以再定义一个 postgresql 包,提供 PostgreSQL 的支持。

4.共享 mock 包

因为我们的依赖项通过我们的 domain 包定义的接口与其他依赖项隔离,所以我们可以使用这些连接点来注入 mock 实现。可以使用 mock 库生成 mock 代码,也可以自己编写 mock 代码。

5.使用 main 包将依赖关系连接起来

最后,我们使用 main 包将这些彼此孤立的包连接起来,将对象需要的依赖注入到对象中。

package main

import (
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
_userHttpDelivery "go_standard/user/delivery/http"
_userRepo "go_standard/user/repository/mysql"
_userUsecase "go_standard/user/usecase"
"xorm.io/xorm"
)

func main() {
db, err := xorm.NewEngine("mysql", "root:root@/go_standard?charset=utf8mb4")
if err != nil {
return
}
r := gin.Default()
userRepo := _userRepo.NewMysqlUserRepository(db)
userUsecase := _userUsecase.NewUserUsecase(userRepo)
_userHttpDelivery.NewUserHandler(r, userUsecase)
}

6.总结

我们遵循以上 4 个规则设计 Go 应用程序,不仅可以有效帮助我们在编写代码时避免循环依赖,还可以提升应用程序的可阅读性、可维护性和可扩展性。

值得一提的是,本文旨在建议团队制定成员都要遵循的规则,作为团队的 Go 应用程序设计标准,而不是建议大家必须遵循本文介绍的 4 个规则。

责任编辑:武晓燕 来源: Golang语言开发栈
相关推荐

2010-03-04 10:11:17

Android手机系统

2012-02-15 14:39:55

GNOME 3

2010-08-12 15:59:23

Flex应用程序

2009-09-03 08:46:55

UML类图Java

2012-03-30 15:47:50

ibmdw

2009-02-25 14:51:05

应用程序设计ASP.NET.NET

2010-08-04 09:34:51

Flex设计

2012-03-20 09:20:40

Go语言

2021-05-21 07:59:40

应用程序设计动态库函数

2009-11-03 10:12:35

2017-10-27 13:30:59

大数据MongoDBeBay

2012-06-14 09:32:13

微软Windows 8

2010-12-13 09:20:00

2010-06-09 10:53:28

UML类图

2023-03-26 00:00:01

应用程序LLM策略

2011-07-05 09:48:02

云计算迁移

2013-10-23 10:45:55

移动中间件最佳实践

2023-12-07 08:22:58

Android应用

2014-05-22 10:03:29

2020-12-28 14:40:47

云计算云应用SaaS
点赞
收藏

51CTO技术栈公众号