Project Structure
Latest update: December 13, 2022

Project Structure #

or the sheer lack of it …

😆 Not so fast. There will be more on this topic in due time.

Don’t overdo it. Start small #

You don’t need a billion packages or a deeply nested directory structure. The Go code can work equally fine with a single main.go file, multiple files in the same package, or several layers of deeply nested packages. You are generally encouraged to start small and branch out only when you see the need for it.

What goes in a package? 📦 #

A Go package is essentially just a directory on disk that holds one or multiple .go files. A package is used for grouping related logic and data structures together, but other than that, no one really enforces what those Go files should contain. Therefore, a Java developer’s first reaction might be to group Go code that performs a similar function across the project. Think of having a package called models that groups all the structs related to modelling data across the project. Or one called queries that groups together all the database queries. Or ones called interfaces, utils, controllers, etc. I guess, you start getting the idea.

Well, while grouping by functionality is OK for some use cases, the Go community’s more preferred way is grouping things by domain or use case. Rather than have all the models grouped together, one might have multiple distinct packages, say registration, shipment, admin, etc, each of which might contain its respective data models, HTTP handlers, business logic, etc., all contained in the same package and isolated form the rest of the code.

Without saying that Go projects should follow Domain-Driven Design (DDD), when a project gets big, splitting by domain is a natural way to package things up as if they were their own self-contained things. This way, if a need arises, each of those packages can become its own “thing” - a service, CLI tool, or a library shared across multiple other projects.

An ideal Go package should provide a specific set of functionalities or capabilities that can be used by other parts of the codebase. It should not simply contain a collection of random code or functions without any clear purpose or functionality. Thus, When naming a Go package, it is crucial to choose a name that accurately describes what the package provides. This allows other developers to easily understand the purpose of the package and how it can be used in their own code. Avoid using vague or generic names that do not convey any information about the package’s capabilities.

Design Philosophy On Packaging
Our Blog is a great source of insights about Go, also referred as "golang". Our main contributor Bill Kennedy is a renowned Go Trainer, consultant, and author.

Sample Project Structure #

What follows is a sample diagram of something I am still trying to formulate in my head. This is why it is not something you should stick to, because it will most likely change based on feedback. It is also not strictly following DDD rules.

Relevant Material #

Domain-Driven Design vs. “Functional Core, Imperative Shell”
Sometimes different paradigms are forces that pull our code in different directions. I’ll try to describe such a situation with concerns…
Hexagonal Architecture: Structuring a project and the influence of granularity
About the granularity of ports and adapters and how it influences the code structure