6 minutes read

When writing code for yourself, it's okay to locate all logic in a simple main file, for example, scripts. However, when working in a team, the best practice is to use a specific structure named the Go project layout.

Go project layout

The Go project layout is a project structure that allows splitting the "brains" and "lungs" into smaller packages and files that you can simply import and reuse in different project parts. Let's look at a possible project layout of the projectName project:

- projectName     // folder with a project inside
 |- .git 
 |- 📁 cmd        // place for project entry point "main.go"
 |- 📁 internal   // code that is used only in the project
   |- 📁 handler
   |- 📁 model
   |- 📁 repository
   |- 📁 service
 |- 📁 pkg        // code that you can import from an external project
 |- 📁 api        // OpenAPI and protocol definitions

When it comes to production services written in Go, for example, Google, Yandex, etc., Go Engineers prefer using the following project layout golang-standards/project-layout. Let's see what Common Structure is used.

Common structure

Folder projectName is our project. We will use its tree structure (folders and files inside) for the explanation as a sample project. The project's root directory is given the project name (projectName). All other sub-directories discussed in this section are application native. Let's become familiar with them.

/cmd

The /cmd folder is usually where the package main and the main.go files reside. It is usually not overwhelmed with redundant logic or structs and contains only turn on and off app behavior.

package main

import (
    ...    // handy imports
)

func main() {
    startApp()
}

/internal

It contains the behind-the-scenes or business logic code you don't have to reuse in different projects or share. In a more formal description, /internal is for private application and library code; you can use this directory to encapsulate your application's core functionality, ensuring that its internals are not exposed or imported unintentionally by other projects. For more details, you can take a look at Go 1.4 Release Notes — Internal Packages.

/pkg

In case some repeated logic needs to be used in many places or a different project, we could, 3... 2... 1..., probably you've already guessed it, store it in /pkg directory so that you and others can use it as a library by external applications.

Other projects will import these libraries expecting them to work, so think twice before you put something here!

For example, imagine someone on the web was working on a project named NikocadoAvocado. The author of this project identified that some logic or function from the project, like Ban(target interface{}) { ... } could be reused by other projects, so the author placed this function in a file ban.go within the /pkg directory, turning it into a utility package. After uploading the project to a platform like GitHub, other developers can download this package using the go get command, facilitating code reuse.

/*
- NikocadoAvocado // external project written by some other developer
 |- 📁 cmd        // place for project entry point "main.go"
 |- 📁 internal   // code that is used only in the project
 |- 📁 pkg        // code that you can import from a foreign project
   |- 📁 ban      // package ban
   |   |- ban.go         // file from package ban with some logic 
   |   |- strike.go      // file from package ban with some logic 
   |- 📁 welvcheese      // another package welvcheese
   |- 📁 twostepsahead   // another package welvcheese
*/

// pkg/ban/ban.go
package ban

func Ban(target interface{}) {
   // perform target Ban
}

A utility package is not self-executable; instead, it enhances the functionality of an executable package (the main one) by providing utility functions and other important assets.

The go get command will fetch the packages from the source repository and install them on the GOPATH directory. Then, using the import statement, we can import it into our program.

Now, suppose you're working on a new project named BanKraccBacc and find the previously mentioned Ban(target interface{}) function useful. Instead of rewriting existing logic, you can incorporate it by downloading the NikocadoAvocado package using go get; this approach allows us to import and use the package in the same way as standard library imports, streamlining our development process:

# 1) Installing dependency
go get github.com/github_username/NikocadoAvocado
/*
- BanKraccBacc  // our new project
 |- 📁 cmd     
 |- 📁 internal // here we can use Ban(target interface{}) by importing it
*/

package internal

import b "github.com/NikocadoAvocado/pkg/ban"

func BanKraccBacc(kb KraccBacc) {
    b.Ban(kb)
}

Note that it's totally fine not to use the /pkg directory in small projects unless you decide that some logic can be reused elsewhere. You can check out Importing packages from remote modules if you want to learn more.

/vendor

Last but not least, /vendor is an application dependencies folder. It contains copies of all packages needed to build and test the main module. Packages imported only by package tests outside the main module are not included. As with go mod tidy and other module commands, build constraints, except ignore, are not considered when creating the /vendor directory. The appropriate way to create the /vendor is to use the go mod vendor command.

There are many more directories that can be found in golang-standards/project-layout. Although the Go community as a whole accepts this standard, some developers may disagree with it. Nevertheless, we encourage you to utilize this standard in your projects. Let's mention the ones that might be useful:

/docs — Design and user documents (In addition to your godoc generated documentation).

/examples — Examples for your applications and/or public libraries.

Conclusion

A project often includes many resources, numerous packages of source files, and other items; finding out what belongs where will be easier if the project is structured correctly. Setting up a proper project directory layout is straightforward, even if it isn't evident. The final layout can just slightly differ from project to project.

In this topic, you learned about the standard Go project layout and its core directories like /cmd and /internal. A small project consisting of a few hundred lines of code may be written with a simple structure when all files are placed in the project's root directory unless you identify that some functionality can be reused elsewhere. Finally, using a specific layout for your app is not obligatory, but remember to look up golang-standards/project-layout when you need one.

6 learners liked this piece of theory. 3 didn't like it. What about you?
Report a typo