a73x

high effort, low reward

← Posts

Go Project Layouts

Do you lay awake at night and consider how to optimally layout your Go project? No...? what about recommending Windows to a friend or colleague?? yeah me either...

I've seen a lot online that shows what I can only describe as endgame enterprise Go project layouts. These layouts are heavily folder-based and only make sense when your project has grown large enough to warrant the verbosity these folders provide. My only problem is that people often try to start there.

A lot of design tells you to think about your project in layers.

If you read The Clean Architecture you get told the layers should be,

  1. entities
  2. use cases
  3. interface adapters
  4. frameworks and drivers.

and that all dependencies should point in (yeah I know, I didn't do a circle so "in" doesn't make sense but I'm sure you can follow).

This is an excellent idea; separation of concerns is good.

So you make your folders.

.
├── drivers
├── entities
├── interfaces
└── usecases

aaand this is an awful idea. I don't even want to go further into this hypothetical layout because it hurts too much.

Find me a project that actually creates these folders, and I'll find you the medium article titled "Clean Code in Go" which spawned it.

The important parts of clean code, are the ideas presented, and how you apply them to a package orientated language. Creating a folder to represent each layer, doesn't really carry much weight here.

As a package orientated language, we want to think and reason about things in terms of packages. Yes there will be a point where you may want to group your packages into some group, but that is mostly ceremonial. Go doesn't care if you're accessing domain/bar or domain/foo/bar. Either will simply be accessed as bar. This means that what matters what's in that package bar. Since everything will be read as bar.Thing i.e import bytes and bytes.Buffer.

So, the package name sets context and expectations. If I grab the json package, I expect that package to do things around json. I'd feel a bit confused if I was able to configure an smtp server.

If you cannot come up with a package name that’s a meaningful prefix for the package’s contents, the package abstraction boundary may be wrong

"but you've still not provided a good example?" well yes

I think the project should grow organically to some degree. What we want to do is write code, and refactoring in Go is fairly cheap.

Start with a main.go and make a Run function or some equivalent which it calls.

func Run() error {
	// actual important stuff here
}
func main() {
	if err := Run(); err != nil {
		log.Fatal(err)
	}
}

This allows you to test your run function in a unit test, and keeps your main func minimal.

As your project grows, you can keep it flat inside the root directory

├── api.go
├── go.mod
├── go.sum
├── main.go
├── rss.go
└── sqlite.go

Even just glancing at that, you can guess that this might be an RSS server, that uses sqlite to back it.

Who knows what

├── drivers
├── entities
├── interfaces
└── usecases

does.

As things evolve you might want to put them in internal to hide them from being imported by other packages, or cmd as you develop multiple binaries. Placing things in internal means you're free to mess around with it, without breaking any public contracts other users rely on. I can't be bothered rewriting my example, so here's a random one I found online; it's probably all right. Server Project

project-root-directory/
  go.mod
  internal/
    auth/
      ...
    metrics/
      ...
    model/
      ...
  cmd/
    api-server/
      main.go
    metrics-analyzer/
      main.go
    ...
  ... the project's other directories with non-Go code

My vague summary is that clean code gives you a north star to follow, an idea of how you want to separate and reason about the packages you create. You don't need to create the entities of abstraction that are also presented. Think about what things do or relate to and create packages for them. You should allow your project to grow organically but don't expect architecture to appear without following a north star.