Go Project Organization
Here’s a rough layout of how I organize my Go project. Some parts are situational and some parts are essential. I’ll go over both in this blog.
A rough layout:
+ basedir
+-- go.mod (module jcheng.org)
+-- hello (empty)
+-- log/
+-- utils/
+-- config/
+-- models/
+-- repositories/
+-- services/
+-- cmd/
+-- hello_app/
+--/cmd/
+-- speak/
+-- email/
+-- sms/
The basedir
Situational.
I have my (personal) projects in a monorepo. It’s an easy way to share code between various Go projects. If you are working on a commercial project, you might want to do things differently. For just myself, I don’t need to deal with the overhead of versioning code.
The ‘hello’ project
Essential.
This maps to jcheng.org/hello
. Each “project” maps to something specific. It might be a library, like my personal test
helpers. Or it might be a service that I deploy to AWS. For this example, hello
is a fictional service.
$project/log
Situational.
Contains just a single file which holds a Logger
class. I might move this into my core project though I tend to write
project-specific logging.
$project/config
Essential.
Contains project-specific Config objects. I create structs for all my configuration because I want them to be strongly typed. They sit here.
$project/model
Essential.
Contains data transfer objects, value objects, and entities used by the project. These code have no business rule and
will be used by other packages on the hello
project. In some $past_job
we’ve called them POJOs (plain old Java
objects).
In Java, I would’ve put some POJOs next to their respective services. Doing so in Go can introduce circular
dependencies. If package_a
needs to refer to structs in package_b
and package_b
als need to refer to structs in
package_a
, this creates a circular import in Go that prevents compilation. Sticking everything in the same package is
one way to prevent this problem.
$project/utils
Situational.
Project specific global functions. Yes, for certain things, non-OOP is useful. The strings
library is a good example.
$project/repositories
Essential.
This is a ficional ‘service’ that needs to talk to some database. Code that interfaces with databases are known as repositories and sit here.
$project/services
Essential.
Services is the middleware between the application and the repositories. Services can import repositories, but repositories should never imoport services.
$project/cmd/…
Situational.
I prefer spf13/cobra
1 to create commands with subcommands. My projects have several commands that simply different
iterations, e.g., hello_beta
, hello_v1
, hello_v2
. Each command will have different subcommands for various
reasons. In our fictional application, the hello_app
commmand has subcommands to say hello by speaking through your
computer speakers, via email, or through SMS.
I find this layout to be easy on my mental model and helps me quickly navigate to relevant parts of the code.