什么是一个合理的布局Go项目的方式

huangapple go评论85阅读模式
英文:

What is a sensible way to layout a Go project

问题

我有一个越来越复杂的Go项目,想要以一种减少痛苦的方式布置文件系统。

有没有一些好的例子可以参考?

英文:

I have a go project that is starting to become more complex, and want to lay the filesystem out in such a way to reduce pain.

Are there some good examples out there of what makes sense?

答案1

得分: 145

2023: 来自**Christoph Berger**的一般建议在“唯一、必备、永恒的Go项目布局”中:

>剧透警告:这样的东西是不存在的。最佳的Go项目布局取决于您的具体用例。

这提供了一个对有争议的(非官方)存储库golang-standards/project-layout的反驳观点:
"#117: 这不是一个标准的Go项目布局"。


2023年5月更新:官方文档在“代码组织”部分中:

> Go代码必须保存在一个工作区中。
工作区是一个目录层次结构,其根目录下有三个目录:
>
> - src 包含按包组织的Go源文件(每个目录一个包),
> - pkg 包含包对象,以及
> - bin 包含可执行命令。
>
> go工具构建源包并将生成的二进制文件安装到pkgbin目录中。
>
> src子目录通常包含多个版本控制存储库(如Git或Mercurial),用于跟踪一个或多个源包的开发。
>
> bin/
> streak # 命令可执行文件
> todo # 命令可执行文件
> pkg/
> linux_amd64/
> code.google.com/p/goauth2/
> oauth.a # 包对象
> github.com/nf/todo/
> task.a # 包对象
> src/
> code.google.com/p/goauth2/
> .hg/ # Mercurial存储库元数据
> oauth/
> oauth.go # 包源代码
> oauth_test.go # 测试源代码


2023年7月更新:参见Ben Johnson的“在Go中构建应用程序的结构”。

该文章包括以下提示:

将二进制文件与应用程序分开

> 将main.go文件和应用程序逻辑组合在同一个包中有两个后果:
>
> - 它使我的应用程序无法作为库使用。
> - 我只能有一个应用程序二进制文件。
>
> 我发现解决这个问题的最佳方法是在项目中使用一个“cmd”目录,其中每个子目录都是一个应用程序二进制文件。
>
> camlistore/
> cmd/
> camget/
> main.go
> cammount/
> main.go
> camput/
> main.go
> camtool/
> main.go

以库为驱动的开发

> 将main.go文件移出根目录可以让您从库的角度构建应用程序。您的应用程序二进制文件只是您应用程序的库的客户端。
>
> 有时,您可能希望用户以多种方式进行交互,因此创建多个二进制文件。
例如,如果您有一个“adder”包,允许用户将数字相加,您可能希望发布一个命令行版本和一个Web版本。
您可以通过以下方式轻松实现此目标:
>
> adder/
> adder.go
> cmd/
> adder/
> main.go
> adder-server/
> main.go
>
> 用户可以使用“go get”命令安装您的“adder”应用程序二进制文件:
>
> $ go get github.com/benbjohnson/adder/...
>
> 然后,您的用户就安装了“adder”和“adder-server”!

不要过度使用子包

> 通常,我的项目类型都是相关的,因此从可用性和API的角度来看,它更适合。
这些类型还可以利用它们之间的非导出调用,从而使API保持简洁明了。
>
> 1. 在每个文件中将相关类型和代码组合在一起。如果您的类型和函数组织得很好,那么我发现文件的行数通常在200到500行之间。这听起来可能很多,但我发现很容易导航。对于单个文件,我的上限通常是1000行。
> 2. 将最重要的类型放在文件的顶部,并将类型按重要性递减的顺序添加到文件的底部。
> 3. 一旦您的应用程序的代码超过10,000行,您应该认真评估是否可以将其拆分为较小的项目。

注意:最后一种做法并不总是好的:

> 很抱歉,我无法同意这种做法。
将类型分离到文件中有助于代码管理、可读性、可维护性和可测试性。
它还可以确保单一职责和开闭原则的遵循...
不允许循环依赖的规则是为了强制我们拥有一个清晰的包结构。


(2023年2月备选方案,仅涉及src
您可以在“GitHub代码布局”中找到经典布局的示例:

> 应用程序和两个库都位于Github上,每个都有自己的存储库。
$GOPATH是项目的根目录-您的每个Github存储库都将被检出到$GOPATH的几个文件夹下。
>
> 您的代码布局如下所示:
>
> $GOPATH/
> src/
> github.com/
> jmcvetta/
> useless/
> .git/
> useless.go
> useless_test.go
> README.md
> uselessd/
> .git/
> uselessd.go
> uselessd_test.go
> README.md
>
> src/github.com/jmcvetta/下的每个文件夹都是一个单独的git检出根目录。

然而,这引起了一些批评,在这个reddit页面中:

> 我强烈建议不要按照您的方式构建存储库,这会破坏“go get”,而这是Go最有用的功能之一。
更好的做法是为那些了解Go的人编写代码,因为他们很可能是编译代码的人。
对于不了解Go的人来说,他们至少可以对这门语言有所了解。
>
> 将主要包放在存储库的根目录中。
将资源放在子目录中(以保持整洁)。
将代码的主要部分放在子包中(以防有人想在您的二进制文件之外重用它)。
在存储库的根目录中包含一个设置脚本,以便易于找到。
>
> 这仍然只是一个两步过程来下载、构建、安装和设置:
>
> - “go get <your repo path>”:下载并安装Go代码,其中包含资源的子目录
> - $GOPATH/<your repo path>/setup.sh:将资源分发到正确的位置并安装服务

英文:

2023: general advice from Christoph Berger in "The one-and-only, must-have, eternal Go project layout":

> Spoiler Alert: There is no such thing. The optimal Go project layout depends on your exact use case.

That offers a counter point to the controversial (and non-official) repository
golang-standards/project-layout, with its contentious issue:
"#117: this is not a standard Go project layout".


Update May 2013: the official documentation is in the section "Code organization"

> Go code must be kept inside a workspace.
A workspace is a directory hierarchy with three directories at its root:
>
> - src contains Go source files organized into packages (one package per directory),
> - pkg contains package objects, and
> - bin contains executable commands.
>
> The go tool builds source packages and installs the resulting binaries to the pkg and bin directories.
>
> The src subdirectory typically contains multiple version control repositories (such as for Git or Mercurial) that track the development of one or more source packages.
>
> bin/
> streak # command executable
> todo # command executable
> pkg/
> linux_amd64/
> code.google.com/p/goauth2/
> oauth.a # package object
> github.com/nf/todo/
> task.a # package object
> src/
> code.google.com/p/goauth2/
> .hg/ # mercurial repository metadata
> oauth/
> oauth.go # package source
> oauth_test.go # test source


Update July 2014: see "Structuring Applications in Go" from Ben Johnson

That article include tips like:

Separate your binary from your application

> combining the main.go file and my application logic in the same package has two consequences:
>
> - It makes my application unusable as a library.
> - I can only have one application binary.
>
> The best way I’ve found to fix this is to simply use a “cmd” directory in my project where each of its subdirectories is an application binary.
>
> camlistore/
> cmd/
> camget/
> main.go
> cammount/
> main.go
> camput/
> main.go
> camtool/
> main.go

Library driven development

> Moving the main.go file out of your root allows you to build your application from the perspective of a library. Your application binary is simply a client of your application’s library.
>
> Sometimes you might want users to interact in multiple ways so you create multiple binaries.
For example, if you had an “adder” package that that let users add numbers together, you may want to release a command line version as well as a web version.
You can easily do this by organizing your project like this:
>
> adder/
> adder.go
> cmd/
> adder/
> main.go
> adder-server/
> main.go
>
> Users can install your “adder” application binaries with “go get” using an ellipsis:
>
> $ go get github.com/benbjohnson/adder/...
>
> And voila, your user has “adder” and “adder-server” installed!

Don’t go crazy with subpackages

> Usually my project’s types are all very related so it fits better from a usability and API standpoint.
These types can also take advantage of calling unexported between them which keeps the API small and clear.
>
> 1. Group related types and code together in each file. If your types and functions are well organized then I find that files tend to be between 200 and 500 SLOC. This might sound like a lot but I find it easy to navigate. 1000 SLOC is usually my upper limit for a single file.
> 2. Organize the most important type at the top of the file and add types in decreasing importance towards the bottom of the file.
> 3. Once your application starts getting above 10,000 SLOC you should seriously evaluate whether it can be broken into smaller projects.

Note: that last practice isn't always good:

> Sorry I just cant agree with this practice.
Separating type to files helps code management, readability, maintenancability, testability.
It may also ensure single responsibility and the follow of open/closed principle…
The rule for not allowing circular dependency is to force we have a clear structure of the packages.


(Alternative February 2013, regarding src only)
You can find the classic layout illustrated in "GitHub Code Layout":

> The app and both libraries live on Github, each in its own repository.
$GOPATH is the root of the project - each of your Github repos will be checked out several folders below $GOPATH.
>
> Your code layout would look like this:
>
> $GOPATH/
> src/
> github.com/
> jmcvetta/
> useless/
> .git/
> useless.go
> useless_test.go
> README.md
> uselessd/
> .git/
> uselessd.go
> uselessd_test.go
> README.md
>
> Each folder under src/github.com/jmcvetta/ is the root of a separate git checkout.

That attracted some criticisms though, in this reddit page:

> I highly recommend not structuring the repo the way you have, it'll break "go get", which is one of the most useful things about Go.
It's far better to write your code for people who do know Go, since they're most likely to be the ones compiling it.
And for people who don't, they'll at least get a feel for the language.
>
> Put the main package in the root of the repo.
Have the assets in a subdirectory (to keep things neat).
Keep the meat of the code in a subpackage (in case anyone wants to reuse it outside your binary).
Include a setup script in the root of the repo so it's easy to find.
>
> It's still only a two step process to download, build, install, and setup.:
>
> - "go get <your repo path>": downloads and installs the go code, with a subdir for the assets
> - $GOPATH/<your repo path>/setup.sh: distributes the assets to the right place and installs the service

答案2

得分: 8

我假设你所说的“项目”并不是指一个Go包,而是你正在开发的软件。
否则,你可以在这里获得帮助(http://golang.org/doc/code.html)和(https://stackoverflow.com/questions/9985559/organizing-a-multiple-file-go-project)。
然而,这与为Go编写包并没有太大的不同:使用包,为每个包创建一个文件夹,并将这些包组合在你的应用程序中。

为了形成自己的观点,你可以查看github上流行的Go存储库:https://github.com/trending/go。值得注意的例子有cayleyzeus

最流行的方案可能是有一个主Go文件和许多模块和子模块在它们自己的目录中。如果你有许多元文件(文档、许可证、模板等),你可能想把源代码放到一个子目录中。这是我到目前为止所做的。

英文:

I assume that with 'project' you don't mean a Go package but a software you develop.
Otherwise you can get help here and here.
However it's not so much different to writing packages for Go: Use packages, create a folder for each package and combine those packages in your application.

To build yourself an opinion, you can look at trending Go repositories on github: https://github.com/trending/go. Notable examples are cayley
and zeus.

The most popular scheme is probably to have a main Go file and many modules and submodules in their own directories. In case you have many meta files (doc, license, templates, ...) you may want to put the source code into a subdirectory. That's what I did so far.

答案3

得分: 1

有一种推荐的方法来自Golang的作者,它定义了如何布局代码以最好地与go工具配合使用并支持源代码控制系统。

英文:

There is a recommended approach from the authors of Golang that defines how to layout your code to work best with the go tools and to support source control systems

答案4

得分: 1

你可能还应该查看这个仓库。它展示了很多关于如何组织Go应用程序的想法:https://github.com/golang-standards/project-layout

英文:

You should probably also have a look into this repo. It shows a lot of ideas how to structure go applications: https://github.com/golang-standards/project-layout

huangapple
  • 本文由 发表于 2013年2月14日 11:43:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/14867452.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定