The layout of a Go application on disk is a question that almost everyone asks when starting out.
It’s a fair question - there’s no scaffolding that’s widely used, there was no guidelines for people for the first decade.
One of the biggest strengths of Go is that you can lay your project out on disk pretty much how you want to.
Unfortunately, though, that strength is its weakness, people lay their projects out on disk pretty much how they want to, leaving people new to the ecosystem scratching their heads.
The official Go blog did finally create a blog post that put some ideas out there for people to use.
Here’s howI think about the layout.
In the first place, the project, for example, has a root directory, that will have the README.md
and go.mod
and go.sum
files in it.
myproject/
├── go.mod
├── go.sum
└── README.md
The project itself has a “core” library, which is typically the business logic for the project, so I put that at the root of the project.
The package
that these files are in is going to be myproject
as well.
myproject/
├── go.mod
├── go.sum
├── myproject.go
├── my_project_test.go
└── README.md
To build the project into a binary there needs to be a main
package. There is a de facto standard (meaning everyone uses this standard, but it’s not an actual standard), and that is to put the main
package in a directory named cmd
.
myproject/
├── cmd
│ └── main.go
├── go.mod
├── go.sum
├── myproject.go
├── my_project_test.go
└── README.md
The next step in my thought process is “supporting libraries”, that is, libraries that my business logic will need in order to do its tasks.
I put these supporting libraries beneath the core library, so that all developers can see that the library at the top of the tree is supported by the libraries below.
There’s a little bit extra happening here, as a firm believer in the SOLID design principles - I create an interface
that is owned by the core library that defines the contract that my datastores need to abide by to be used.
For this example the interface will be in feed.go
, and the feeds
directory holds the code that creates types that satisfy that interface and allow the core library to interact with the upstream data feeds (each in their own directory).
myproject/
├── cmd
│ └── main.go
├── feeds
│ ├── twitter
│ │ ├── twitter.go
│ │ └── twitter_test.go
│ └── reddit
│ ├── reddit.go
│ └── reddit_test.go
├── feed.go
├── go.mod
├── go.sum
├── myproject.go
├── my_project_test.go
└── README.md
I used to put the feed.go
file, with interface definition, within the feeds
directory, to show that that wasthe interface that the feeds need to satisfy in order to be used by the core library, but having the definition in the core library demonstrates that the core library owns the interface (which it should).
This should be enough to demonstrate the layout of a project, with the only other thing to mention being that if the supporting libraries themselves need supporting libraries, then those secondary support libraries should go below the libraries that they support.