-
Notifications
You must be signed in to change notification settings - Fork 337
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
_content/doc: add guide for organizing a Go module (project layout)
Change-Id: I2092a70e0be3dbbd08d0b13c4bd4bd073a504cde Reviewed-on: https://go-review.googlesource.com/c/website/+/529415 Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Russ Cox <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
- Loading branch information
Showing
2 changed files
with
293 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
<!--{ | ||
"Title": "Organizing a Go module" | ||
}--> | ||
|
||
A common question developers new to Go have is "How do I organize my Go | ||
project?", in terms of the layout of files and folders. The goal of this | ||
document is to provide some guidelines that will help answer this question. To | ||
make the most of this document, make sure you're familiar with the basics of Go | ||
modules by reading [the tutorial](/doc/tutorial/create-module) and | ||
[managing module source](/doc/modules/managing-source). | ||
|
||
Go projects can include packages, command-line programs or a combination of the | ||
two. This guide is organized by project type. | ||
|
||
### Basic package | ||
|
||
A basic Go package has all its code in the project's root directory. The project | ||
consists of a single module, which consists of a single package. The package | ||
name matches the last path component of the module name. For a very simple | ||
package requiring a single Go file, the project structure is: | ||
|
||
``` | ||
project-root-directory/ | ||
go.mod | ||
modname.go | ||
modname_test.go | ||
``` | ||
|
||
_[throughout this document, file/package names are entirely arbitrary]_ | ||
|
||
Assuming this directory is uploaded to a GitHub repository at | ||
`github.com/someuser/modname`, the `module` line in the `go.mod` file should say | ||
`module github.com/someuser/modname`. | ||
|
||
The code in `modname.go` declares the package with: | ||
|
||
``` | ||
package modname | ||
// ... package code here | ||
``` | ||
|
||
Users can then rely on this package by `import`-ing it in their Go code with: | ||
|
||
``` | ||
import "github.com/someuser/modname" | ||
``` | ||
|
||
A Go package can be split into multiple files, all residing within the same | ||
directory, e.g.: | ||
|
||
``` | ||
project-root-directory/ | ||
go.mod | ||
modname.go | ||
modname_test.go | ||
auth.go | ||
auto_test.go | ||
hash.go | ||
hash_test.go | ||
``` | ||
|
||
All the files in the directory declare `package modname`. | ||
|
||
### Basic command | ||
|
||
A basic executable program (or command-line tool) is structured according to its | ||
complexity and code size. The simplest program can consist of a single Go file | ||
where `func main` is defined. Larger programs can have their code split across | ||
multiple files, all declaring `package main`: | ||
|
||
``` | ||
project-root-directory/ | ||
go.mod | ||
auth.go | ||
auth_test.go | ||
client.go | ||
main.go | ||
``` | ||
|
||
Here the `main.go` file contains `func main`, but this is just a convention. The | ||
"main" file can also be called `modname.go` (for an appropriate value of | ||
`modname`) or anything else. | ||
|
||
Assuming this directory is uploaded to a GitHub repository at | ||
`github.com/someuser/modname`, the `module` line in the `go.mod` file should | ||
say: | ||
|
||
``` | ||
module github.com/someuser/modname | ||
``` | ||
|
||
And a user should be able to install it on their machine with: | ||
|
||
``` | ||
$ go install github.com/someuser/modname@latest | ||
``` | ||
|
||
### Package or command with supporting packages | ||
|
||
Larger packages or commands may benefit from splitting off some functionality | ||
into supporting packages. Initially, it's recommended placing such packages into | ||
a directory named `internal`; | ||
[this prevents](https://pkg.go.dev/cmd/go#hdr-Internal_Directories) other | ||
modules from depending on packages we don't necessarily want to expose and | ||
support for external uses. Since other projects cannot import code from our | ||
`internal` directory, we're free to refactor its API and generally move things | ||
around without breaking external users. The project structure for a package is | ||
thus: | ||
|
||
``` | ||
project-root-directory/ | ||
internal/ | ||
auth/ | ||
auth.go | ||
auth_test.go | ||
hash/ | ||
hash.go | ||
hash_test.go | ||
go.mod | ||
modname.go | ||
modname_test.go | ||
``` | ||
|
||
The `modname.go` file declares `package modname`, `auth.go` declares `package | ||
auth` and so on. `modname.go` can import the `auth` package as follows: | ||
|
||
``` | ||
import "github.com/someuser/modname/internal/auth" | ||
``` | ||
|
||
The layout for a command with supporting packages in an `internal` directory is | ||
very similar, except that the file(s) in the root directory declare `package | ||
main`. | ||
|
||
### Multiple packages | ||
|
||
A module can consist of multiple importable packages; each package has its own | ||
directory, and can be structured hierarchically. Here's a sample project | ||
structure: | ||
|
||
``` | ||
project-root-directory/ | ||
go.mod | ||
modname.go | ||
modname_test.go | ||
auth/ | ||
auth.go | ||
auth_test.go | ||
token/ | ||
token.go | ||
token_test.go | ||
hash/ | ||
hash.go | ||
internal/ | ||
trace/ | ||
trace.go | ||
``` | ||
|
||
As a reminder, we assume that the `module` line in `go.mod` says: | ||
|
||
``` | ||
module github.com/someuser/modname | ||
``` | ||
|
||
The `modname` package resides in the root directory, declares `package modname` | ||
and can be imported by users with: | ||
|
||
``` | ||
import "github.com/someuser/modname" | ||
``` | ||
|
||
Sub-packages can be imported by users as follows: | ||
|
||
``` | ||
import "github.com/someuser/modname/auth" | ||
import "github.com/someuser/modname/auth/token" | ||
import "github.com/someuser/modname/hash" | ||
``` | ||
|
||
Package `trace` that resides in `internal/trace` cannot be imported outside this | ||
module. It's recommended to keep packages in `internal` as much as possible. | ||
|
||
### Multiple commands | ||
|
||
Multiple programs in the same repository will typically have separate directories: | ||
|
||
``` | ||
project-root-directory/ | ||
go.mod | ||
internal/ | ||
... shared internal packages | ||
prog1/ | ||
main.go | ||
prog2/ | ||
main.go | ||
``` | ||
|
||
In each directory, the program's Go files declare `package main`. A top-level | ||
`internal` directory can contain shared packages used by all commands in the | ||
repository. | ||
|
||
Users can install these programs as follows: | ||
|
||
``` | ||
$ go install github.com/someuser/modname/prog1@latest | ||
$ go install github.com/someuser/modname/prog2@latest | ||
``` | ||
|
||
A common convention is placing all commands in a repository into a `cmd` | ||
directory; while this isn't strictly necessary in a repository that consists | ||
only of commands, it's very useful in a mixed repository that has both commands | ||
and importable packages, as we will discuss next. | ||
|
||
### Packages and commands in the same repository | ||
|
||
Sometimes a repository will provide both importable packages and installable | ||
commands with related functionality. Here's a sample project structure for such | ||
a repository: | ||
|
||
``` | ||
project-root-directory/ | ||
go.mod | ||
modname.go | ||
modname_test.go | ||
auth/ | ||
auth.go | ||
auth_test.go | ||
internal/ | ||
... internal packages | ||
cmd/ | ||
prog1/ | ||
main.go | ||
prog2/ | ||
main.go | ||
``` | ||
|
||
Assuming this module is called `github.com/someuser/modname`, users can now both | ||
import packages from it: | ||
|
||
``` | ||
import "github.com/someuser/modname" | ||
import "github.com/someuser/modname/auth" | ||
``` | ||
|
||
And install programs from it: | ||
|
||
``` | ||
$ go install github.com/someuser/modname/cmd/prog1@latest | ||
$ go install github.com/someuser/modname/cmd/prog2@latest | ||
``` | ||
|
||
### Server project | ||
|
||
Go is a common language choice for implementing *servers*. There is a very large | ||
variance in the structure of such projects, given the many aspects of server | ||
development: protocols (REST? gRPC?), deployments, front-end files, | ||
containerization, scripts and so on. We will focus our guidance here on the | ||
parts of the project written in Go. | ||
|
||
Server projects typically won't have packages for export, since a server is | ||
usually a self-contained binary (or a group of binaries). Therefore, it's | ||
recommended to keep the Go packages implementing the server's logic in the | ||
`internal` directory. Moreover, since the project is likely to have many other | ||
directories with non-Go files, it's a good idea to keep all Go commands together | ||
in a `cmd` directory: | ||
|
||
``` | ||
project-root-directory/ | ||
go.mod | ||
internal/ | ||
auth/ | ||
... | ||
metrics/ | ||
... | ||
model/ | ||
... | ||
cmd/ | ||
api-server/ | ||
main.go | ||
metrics-analyzer/ | ||
main.go | ||
... | ||
... the project's other directories with non-Go code | ||
``` | ||
|
||
In case the server repository grows packages that become useful for sharing with | ||
other projects, it's best to split these off to separate modules. |