Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go 应用程序设计标准

Go 应用程序设计标准

作者头像
frank.
发布于 2022-05-17 03:47:50
发布于 2022-05-17 03:47:50
23400
代码可运行
举报
运行总次数:0
代码可运行

01

介绍

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

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

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

02

定义 domain 包

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 包。

03

按照依赖关系划分包

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

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 的支持。

04

共享 mock 包

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

05

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)
}

06

总结

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

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

参考资料:

  1. https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1
  2. https://github.com/bxcodec/go-clean-arch/pull/21
  3. https://github.com/golang-standards/project-layout/issues/117
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go语言开发栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
编辑精选文章
换一批
Golang 单元测试 - 数据层
数据层,就是我们常常说的 repo/dao,其功能就是和数据库、缓存或者其他数据源打交道。它需要从数据源中获取数据,并返回给上一层。在这一层通常没有复杂业务的逻辑,所以最重要的就是测试各个数据字段的编写是否正确,以及 SQL 等查询条件是否正常能被筛选。
LinkinStar
2023/02/22
1K0
Go 语言微服务框架 Kratos 操作 MySQL 和 Redis
Go 语言微服务框架 Kratos 不限制使用任何第三方库,我们可以根据个人喜好选择第三方库。
frank.
2024/12/30
1480
Go 语言微服务框架 Kratos 操作 MySQL 和 Redis
Go 语言微服务框架 Kratos 开发 HTTP API
本文我们分为开发环境,创建项目,代码目录,HTTP API,四个部分介绍 Kratos 微服务框架。
frank.
2024/12/23
3590
Go 语言微服务框架 Kratos 开发 HTTP API
Go 语言错误处理为什么更推荐使用 pkg/errors 三方库?
Go 语言项目开发中,我们通常需要在代码逻辑中进行错误处理,Go 官方标准库 errors 为我们提供了一些方法,比如 New,Unwarp,Is 和 As。
frank.
2022/07/01
8760
Gin 框架之Cookie与Session
早期(十几年前)的网页是静态的, 数据都是写死的, 人们访问网页只是用来查看新闻的, 没有保存用户状态的需求。
贾维斯Echo
2024/01/19
3350
Gin 框架之Cookie与Session
从理论到实践:Go 项目中的整洁架构设计
你维护的 Go 项目代码架构是什么样子的?六边形架构?还是洋葱架构?亦或者是 DDD?无论项目采用的是什么架构,核心目标都应是一致的:使代码能够易于理解、测试和维护。
陈明勇
2024/12/06
3887
从理论到实践:Go 项目中的整洁架构设计
Go 语言微服务框架 Kratos 集成第三方库 kafka-go 操作消息队列 Kafka
Go 语言微服务框架 Kratos 不限制使用任何第三方库,Go 语言操作消息队列 Kafka 有很多优秀的第三方库,比如 sarama 和 kafka-go,我们在之前的文章中介绍过 Go 语言怎么使用 sarama 操作消息队列 Kafka。
frank.
2025/01/06
970
Go 语言微服务框架 Kratos 集成第三方库 kafka-go 操作消息队列 Kafka
Go:微服务架构下的单元测试(基于 Ginkgo、gomock 、Gomega)
本文主要使用 Ginkgo[2] 、gomock[3] 、Gomega[4] 工具来实现单元测试,之前不了解的同学,可以先熟悉一下相关文档。
Freedom123
2024/03/29
5880
Go:微服务架构下的单元测试(基于 Ginkgo、gomock 、Gomega)
Golang 单元测试 - 逻辑层
前面我们完成了最麻烦的数据层的单元测试,今天我们来看看单元测试中最容易做的一层,数据逻辑层,也就是我们通常说的 service 或者 biz 等,是描述具体业务逻辑的地方,这一层包含我们业务最重要的逻辑。
LinkinStar
2023/02/22
5150
Go 单元测试之mock接口测试
gomock 是一个 Go 语言的测试框架,在实际项目中,需要进行单元测试的时候。却往往发现有一大堆依赖项。这时候就是 Gomock 大显身手的时候了,用于编写单元测试时模拟和测试依赖于外部服务的代码。它允许你创建模拟对象(Mock Objects),这些对象可以预设期望的行为,以便在测试时模拟外部依赖,通常使用它对代码中的那些接口类型进行mock。
贾维斯Echo
2024/04/19
2310
Go 单元测试之mock接口测试
Golang 简洁架构实战
由于golang不像java一样有一个统一的编码模式,所以我们和其他团队一样,采用了 Go 面向包的设计和架构分层这篇文章介绍的一些理论,然后再结合以往的项目经验来进行分包:
luozhiyun
2022/05/09
1.2K0
Golang 简洁架构实战
聊聊go-ddd-sample
go-ddd-sample分为application、domain、infrastructure、interfaces四层,其中domain定义repository接口,infrastructure层实现该接口,application层通过domain来编排业务逻辑,interfaces层则基于net/http来提供http接口。
code4it
2021/03/23
7110
聊聊go-ddd-sample
Gin 框架之用户密码加密
Gin是一个用Go语言编写的Web框架,而用户密码的加密通常是在应用程序中处理用户身份验证时的一个重要问题。
贾维斯Echo
2024/01/19
3090
Gin 框架之用户密码加密
基于框架gin+xorm搭建的MVC项目
go-gin-mvc 项目地址:https://github.com/mydevc/go-gin-mvc
李海彬
2019/05/08
2K0
我做了一个 Go 语言的微服务工具包
多年以来,我一直认为自己是一名语言无关的软件开发人员,因为在编程语言方面,我总是把掌握基础知识和学习新概念放在首位,而不是“玩最爱”。在我 15 年的职业生涯中,我已经用多种语言(例如 Java、Scala、Go 等)编写了数千行代码。直到我精通 Go 之后,我才意识到:选择正确的语言很重要。我成为了一名真正的忠实主义者;今天,它无疑是我最喜欢的语言。它的简单、优雅以及强大的并发范式使其非常适用于下一代的分布式服务。
深度学习与Python
2021/03/12
8280
我做了一个 Go 语言的微服务工具包
Go 语言中怎么使用依赖注入?
在 Go 语言项目开发中,我们处理组件层级之间的依赖关系时,通常我们会先在依赖层级的代码中实例化被依赖层级,然后调用它的方法,即依赖方需要主动获取被依赖方。
frank.
2024/04/02
1640
Go 语言中怎么使用依赖注入?
Golang 单元测试 - 接口层
上次我们已经搞定了逻辑层的单元测试,这次我们来康康接口层的单元测试。接口层主要负责的就是请求的处理,最常见的就是 HTTP 请求的处理。
LinkinStar
2023/02/24
4080
go使用消息队列优化接口
在我们编写后端接口时,通常有些接口对于实时性的要求并不是那么高,但其中有些函数却相当占用接口调用时间,如调用第三方接口、发送短信、发送邮件等等。为了提升用户的体验感、系统的稳定性,此时我们就可以使用消息队列对于接口进行优化,对于实时性要求不高的接口使用消息队列来进行处理,提高api响应速度,优化用户体验。本文将以go语言使用rabbitMQ来演示如何对于一个接口进行优化。
陈杪秋
2024/07/22
1080
Golang工程经验(上)
作为一个C/C++的开发者而言,开启Golang语言开发之路是很容易的,从语法、语义上的理解到工程开发,都能够快速熟悉起来;相比C、C++,Golang语言更简洁,更容易写出高并发的服务后台系统
李海彬
2018/10/08
2K1
Golang工程经验(上)
Golang 语言编写 gRPC 实战项目
在之前的几篇文章中,我们介绍了 protobuf 和 grpc,本文我们介绍怎么使用 grpc 开发“分布式系统”。这里使用引号是因为分布式系统是一个大概念,本文我们先介绍使用 grpc 开发分布式系统中的 service。
frank.
2021/09/29
3.2K0
相关推荐
Golang 单元测试 - 数据层
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验