您现在的位置是:亿华云 > IT科技
聊聊Go 应用程序设计标准
亿华云2025-10-03 02:09:44【IT科技】4人已围观
简介1.介绍众所周知 Go 语言官方成员 Russ Cox 曾向 Go 社区回应并没有 Go 应用程序设计标准。但是,为什么本文还要使用这个标题呢?因为团队达成一个共识(标准),制定一些团队成员都要遵循的
众所周知 Go 语言官方成员 Russ Cox 曾向 Go 社区回应并没有 Go 应用程序设计标准。聊聊但是应用,为什么本文还要使用这个标题呢?程序
因为团队达成一个共识(标准),制定一些团队成员都要遵循的设计规则,可以使我们的标准应用程序更容易维护。本文介绍一下我们应该怎么组织我们的聊聊代码,制定团队的应用 Go 应用程序设计标准。
需要注意的程序是,它不是设计核心 Go 开发团队制定的官方标准。
为什么需要定义 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 个规则。
很赞哦!(62125)