Golang_项目分层
文章目录
golang 项目分层
一个基本的go项目一般会有cmd
, internal
, pkg
三个基础目录来分层,当然这不是官方go
核心开发团队定义的标准。但这个确实是目前go
生态系统中比较常见的布局形式,不管从之前的和还是现在开发项目的分层来看。这些基础目录同样适用更大的项目,并且还有一些小的增强功能。
反模式理解
之前go项目的三方依赖包的管理最早有vendor, go dep
等等,但都不是官方的,使用起来也不是尽善尽美,并不能像java项目maven那样的粒度管理三方依赖包。但随着go 1.14正式发布,go modules
管理三方依赖包的工具也正式发布了。请尽量使用go modules, 除非你有一定不用他的理由。用go modules,你就不用关心GOPATH和非要把你的项目放在go workspace文件夹了。如果你创建一个项目来学习go或你开发的是一个PoC或很小的项目,这种分层就没必要使用了,可能一个main.go
文件就够了,即把数据、业务逻辑、规则、路由等等全部放在这个文件即可,也是所谓的反模式。
应用项目是包含了很多需要部署在一起的程序集,包括服务、命令行工具和后台运行的程序。每个项目都对应一个含有其所有源代码的仓库,包括所有依赖的三方包。你需要几个应用项目,视情况以你而定,当然是越少越好。
每个应用项目通常包含三个根目录,分别是 cmd, internal, pkg, vendor
。在 internal 文件里也会包含 pkg
目录,但是它和 internal 里其他的包有着不同的设计约束。
一个典型的应用项目结构应该是这样的:
分包的职责
cmd/
项目中的所有你将要编译成可执行程序的入口代码都放在cmd/
文件夹里,这些代码和业务没有关系。每个程序对应一个文件夹,文件夹的名称应该以程序的名称命名。一般在名称后面加上d
代表该程序是一个守护进程运行。 每个文件夹必须有一个main
包的源文件,该源文件的名称也最好命名成可执行程序的名称,当然也可以保留main文件名。在此会导入和调用internal/
和pkg/
等其他文件夹中相关的代码。 【这个应该是处理各种配置的】
internal/ 【私有包,不允许其他项目试用】
在go语言中,变量,函数,方法等的存取权限只有exported(全局)和unexported(包可见,局部)2种。
在项目中不被复用,也不能被其他项目导入,仅被本项目内部使用的代码包即私有的代码包都应该放在internal
文件夹下。该文件夹下的所有包及相应文件都有一个项目保护级别,即其他项目是不能导入这些包的,仅仅是该项目内部使用。
如果你在其他项目中导入另一个项目的internal
的代码包,保存或go build
编译时会报错use of internal package ... not allowed
,该特性是在go 1.4版本开始支持的,编译时强行校验。
internal/pkg/
在同一个项目中不同程序需要访问,但又不能让其他项目访问的代码包,需要放在这里。这些包是比较基础但又提供了很特殊的功能,比如数据库、日志、用户验证等功能。
pkg/
如果你把代码包放在根目录的pkg
下,其他项目是可以直接导入pkg
下的代码包的,即这里的代码包是开放的,当然你的项目本身也可以直接访问的。但是如果你要把代码放在pkg
下,还想需要三思而后行吧,有没必要这样做,毕竟internal
目录是最好的方式保护你的代码并且被go编译器强制校验internal
的代码包不可分享的。如果你的项目是一个开源的并且让其他人使用你封装的一些函数等,这样做是合适的,如果你自己或公司的某一个项目,个人的经验,基本上用不上pkg
面向包的设计和验证
面向包设计的准则可以验证项目中包设计的是否合理,下面这些步骤可以帮你发现包设计的问题。
包的位置
kit
被不同应用项目导入的基础包cmd
支持编译不同二进制程序的包,比如Restful路由程序,需要相关router, handler包和main入口包。internal
项目内部使用的包,包括crud, service(facade)和业务逻辑的包。internal/pkg
为本项目内部使用的基础包,包括数据库、认证和序列化等操作的包。pkg
其他项目可以访问pkg的代码包
依赖包导入
-
根据业务合理设计包的粒度。
-
在一个包中导入另一个包中的类型,是不合适的。 go源码里面的网络方面的
Request, Response, Header
等都在http
包下面go的设计本身不建议建一个model模块,里面全是一个个结构体。因为这样设计,让其他人看代码,可能不知道这些结构体在哪被使用,修改了结构体,也不知道影响面有多大。
-
在同一个目录级别下的包互相导入,是不合适的。
go更多是按照功能职责进行包的设计,所以同一目录级别下的包是不能互相导入的。除非你采用了在其他语言的架构分层是可以导入的,但也仅限上层可以导入下层的代码包,比如服务层、展现层、业务逻辑层、数据持久化层。
应用级别的策略
比如给restful api的handler写中间件、定时更新等策略。
在Kit
, internal/pkg/, pkg/
中是不允许写这些策略的,也不允许日志的打印,因为这些都是某种意义上共用通用的代码包。在这里数据库的配置、日志文件的配置应该和运行时环境的改变是松散耦合的,可以通过环境变量来修改配置。
在cmd/
, internal/
是可以写中间件和定时器等。
错误处理
错误处理包括错误信息的日志输出,分析和解决错误,并且保证程序能恢复如果发生了错误。
-
Kit
不允许使用
panic
终止程序或抛出错误。 不允许再次包装错误信息,原本原样的把系统错误或框架的错误返回即可。 -
cmd/
允许使用
panic
终止程序或抛出错误。 如果有错误发生且不处理,可以根据此时的业务或逻辑上下文包装一下错误,让更上层的处理错误的函数能知道是哪里抛出的错误。 当然大多数的错误都应该在这里处理。 -
internal/
不允许使用
panic
终止程序或抛出错误。 如果有错误发生且不处理,可以根据此时的业务或逻辑上下文包装一下错误,让更上层的处理错误的函数能知道是哪里抛出的错误。 当然大多数的错误都应该在这里处理。 -
internal/pkg/
不允许使用
panic
终止程序或抛出错误。 不允许再次包装错误信息,原本原样的把系统错误或框架的错误返回即可。 -
pkg/
不允许使用panic
终止程序或抛出错误。 不允许再次包装错误信息,原本原样的把系统错误或框架的错误返回即可。
文章作者 LYR
上次更新 2021-08-14