A Go project may contain multiple files with different package names. Setting up a proper layout in these cases is not always very straightforward in Go, especially for beginners. This Go programming tutorial provides a quick overview, with a hands on example, of specific use case scenarios so that one is able to not only understand the concept behind, but also be able to create a proper layout for a Go project.
There are no strict rules for the directory structure or for how to organize Go project files in a specific way. This is actually both a good and a bad idea; it is bad because it is easy to create a mess and good because the organizational structure of one’s project can be built according to the taste of the programmer.
Freedom without responsibility, however, can be a mess. Go programmers typically follow certain patterns in laying out the files and directories in their projects. This also varies from project to project. They follow these patterns because it works not only for them, but also for their fellow programmers. Everybody following a specific system is not only productive, but also fun to work with. Before going into the project layout, let’s understand some of the basics elements that we come across in relation to Go projects. For starters, one of them is a module.
What is a Module in Go?
In a typical Go project, the first thing a developer should do is create a directory with the project name. Although there are no strict rules, programmers should try to keep the directory name the same as the project name. This directory will contain every file and other subdirectories related to the project:
$ mkdir go-demoproject $ cd go-demoproject
The next thing a Go developer typically does is use go tool commands related to the module. For instance, if we want to initialize new modules in the current directory. As an example, if we want to initialize modules with github.com/xyzuser/go-demoproject we may write the following:
go mod init github.com/xyzuser/go-demoproject
This will create two files in the current directory: go.mod and go.sum. Both are actually simple text files and can be opened with any text editor.
Therefore, a module by definition is a collection of Go packages stored in a file tree with a go.mod file at its root. The go.mod file defines the module path from where dependent third party files are imported, as well as, other modules that are needed to successfully build the application. This is more or less the same as namespaces used in C++, that separate applications in one module with the same application with another module, probably due to different version numbers.
The go.mod File in Go
Go modules are defined by the go.mod file, which describes module properties, the Go version, and the dependencies of this project on other modules. The properties includes:
- Module path of the current module, location from which the module can be downloaded by go tools such as module code’s repository location. This also serves as a unique identifier in case of multiple module’s version numbers. Also includes the prefix of the package path of all packages in the module.
- Minimum Go version number required for the current module.
- Optional instructions on how to replace the current module with another module version.
Suppose, in our go-demoproject, we have dependencies on some other modules such as gorilla/mux, gorm, and MySQL as the backend database. These third party modules need to be downloaded from their respective repositories into a module cache of the local machine. The modules are copied to our project when building the application. So we typically type the following commands:
go get "github.com/jinzhu/gorm" go get "github.com/jinzhu/gorm/dialects/mysql" go get "github.com/gorilla/mux"
The modules are actually downloaded and stored by default in the go subdirectory, located at the home directory of the local machine. The directives in the go.mod file now look something like this:
go.mod module github.com/xyzuser/go-demoproject go 1.18 require ( github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/jinzhu/gorm v1.9.16 // indirect github.com/jinzhu/inflection v1.0.0 // indirect )
As we can see, the file defines:
- Module path
- The version of Go used to create this module file
- Project dependency requirement for successful build and locks them to the specific version number
Notice the suffix – // indirect. The dependency module can be of two types: direct and indirect:
- If the dependency of the module is directly imported it is a direct dependency
- If the direct dependency of the module imports some other dependent modules it is indirect dependency. If a module is mentioned in the go.mod file but not imported by any source code file of the module then also it is treated as indirect dependency
The go.sum File in Go
The go.sum file is another auto-generated dependencies lock file that lists direct and independent dependencies required for the project along with their version number. By the way, isn’t the go.mod file enough for a successful build of the application? The go.sum file lists extra information, such as checksum to validate with the checksum of each direct and indirect dependencies.
The go-demoproject that we have been creating has the following autogenerated go.sum file. This file is generated automatically as we use the command go mod init. These are the sample lines from the auto generated list in my case:
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= ...
Laying out Project Files in Go
A better way to organize a Go project is to put relevant Go code files into a subdirectory under the main directory for the project so that other parts of the project are able to find the APIs and use them. Keeping all source files under the same directory is not a very good idea, even though you can do it. This leverages clean and uncluttered code – something which is very important, as more experienced coders will know.
Now, coming back to our go-demoproject. Let us organize the directory structure. Since, by the looks of it (as we have imported gorilla/mux and gorm and mysql dialects), the project is a web application with a backend database, we like to set the directory tree structure as follows. Understand that this is a sample guideline – it is alright if a programmer chooses to do it in a different way, but it should be done meaningfully and logically consistent. That is the point.
As mentioned, this is the directory tree structure of the sample, yet a specific project type. The root directory of the project is given the project name (go-demoproject). All others are subdirectories and sub subdirectories of this directory. The cmd folder contains the package main and which, in turn, contains the main.go file, from which the execution starts. The pkg subdirectory contains all the local packages that we will use in the application; they are given the relevant names of their content files. Note that the go.mod and go.sum files are directly created under the project root directory.
Final Thoughts on Go Package Layouts
In this Go programming tutorial we have tried to provide information and tips about how to layout the project directory structure in Go. A project typically contains multiple source files arranged into multiple packages and other resources. Unless properly organized, this can be a nightmare to figure out with regards to what goes where. Although not obvious, it is actually simple to lay out a proper project directory structure. A simple tip or a pointer to the right direction can solve a lot of problems, at least in the initial phases. One more tip though – the naming scheme for directories and files should be simple and meaningful and should be placed in properly named packages. That’s all for now. Happy Going!
Read more Go and Golang programming tutorials.