最近在重构一个早期实现的命令行工具项目。在修改项目结构的过程中,没有看到Go语言项目结构的最佳实践。其他语言都有这样的推荐项目结构。比如Java有一个典型的项目结构,开发者可以在这个项目结构下进行开发。Python使用Django和Flask时,框架会直接定义项目结构。但Go社区实际上尚未就项目结构达成一致。虽然已经有一些推荐的项目结构,但是一般推荐的结构并不适合我的项目。本文探讨了我的项目的最终结构,并将其与标准最佳实践进行了比较。使用良好的包设计构建项目第一个最佳实践是将项目中的任何可重用代码作为包。如何设计包结构和包的最佳时间需要单独写。我已经分享过一次这个内容。ppt链接如下:https://go-pkg-structure.dev/将代码打成一个A包比仅仅复用代码要有益得多。从项目结构来看,将代码放入独立的包中,有助于将功能独立的代码进行分组,方便其他参与开发的开发人员维护代码,对开源项目具有重要意义。独立于单个包的做法可以使项目更易于测试。将函数分离到单独的包中,这样每个函数都可以在更少的依赖项下进行测试。在创建和重构项目时,我做的第一件事就是编写项目所需的包,甚至在编写代码之前创建基本的项目结构。将应用程序逻辑与访问层逻辑分开我看到的另一个经常使用的最佳实践是将应用程序代码与访问层代码分开,其中访问代码指的是主包和main()方法。Go和其他语言一样。应用的接入层代码是main方法。当应用程序开始运行时,它是要执行的逻辑的第一部分。很可能所有的初始化逻辑都只写在main方法中。最好在app包中实现各自的初始化逻辑,而不是全部写在main方法中。初始化逻辑最好放在各自的包中,这样也更方便测试。比如将Start()、Stop()、Shutdown()方法放在app包中,编写测试代码时就可以调用当前包中的start和stop函数。下面是应用包中的一个实现示例:如果你的命令行工具项目既有服务端代码也有客户端代码,那么在一个app文件夹中实现的逻辑可以被服务端和客户端共享。但是,这种方式对于简单的命令行应用程序并不友好,可能会处于启动-执行-停止模式。但是我还是选择使用将逻辑放在app目录下的方式,这样可以把运行时逻辑放在一起,降低其他开发者理解这个项目的难度。什么应该放在主包中?把我们所有的应用都放到app包里之后,还要考虑一下main包里有什么。很简单,主包里面的内容很少。一般来说,我会将主包限制为“仅与用户交互的代码”。比如我的项目有cli和server端逻辑,我一般会把命令行参数解析的逻辑放在main包里。服务器和客户端cli编译的二进制文件将包含不同的包。通过解析主包中的参数,可以为不同的clis创建独立的选项。其他需要与用户交互的命令行应用,我也倾向于放在main包中,例如:解析用户输入的命令行参数(很简单的输入,不涉及核心逻辑)解析配置文件退出逻辑处理信号下面的代码是一个main方法的例子://mainrunsthecommand-lineparsingandvalidations.Thisfunctionwillalsostarttheapplicationlogicexecution.funcmain(){//Parsecommand-lineargumentsvaroptsoptionsargs,err:=flags.ParseArgs(&opts,os.Args[1:])iferr!=nil{os.Exit(1)}//Converttointernalconfigcfg:=config.New()cfg.Verbose=opts.Verbose//moretakingcommandlineoptionsandputtingthemintoaconfigstruct.ifopts.Pass{//asktheuserforapassword}//RuntheApperr=app.Run(cfg)iferr!=nil{//dostuffos.Exit(1)}}一个推荐的项目结构推荐很多的项目结构如下:internal/app-核心应用程序功能仅供内部使用internal/pkg/-packagepkg/仅供内部使用-Packagecmd/
