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:
+-- go.mod (module jcheng.org)
+-- hello (empty)
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
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.
Contains just a single file which holds a
Logger class. I might move this into my core project though I tend to write
Contains project-specific Config objects. I create structs for all my configuration because I want them to be strongly
typed. They sit here.
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
In Java, I would’ve put some POJOs next to their respective services. Doing so in Go can introduce circular
package_a needs to refer to structs in
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 specific global functions. Yes, for certain things, non-OOP is useful. The
strings library is a good example.
This is a ficional ‘service’ that needs to talk to some database. Code that interfaces with databases are known as
repositories and sit here.
Services is the middleware between the application and the repositories. Services can import repositories, but
repositories should never imoport services.
spf13/cobra1 to create commands with subcommands. My projects have several commands that simply different
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.