1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="Home for a73x" />
<meta name="author" content="a73x" />
<meta name="viewport"
content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width" />
<title>a73x</title>
<link rel="stylesheet" href="/static/styles.css">
<link rel="stylesheet" href="/static/syntax.css">
</head>
<body>
<main>
<div class="header">
<div class="header-title">
<h1>a73x</h1>
<sub>high effort, low reward</sub>
</div>
<nav class="nav">
<ul>
<li><a class="no-decorations" href="/">home</a></li>
<li><a class="no-decorations" href="/posts">posts</a></li>
<li><a class="no-decorations" href="/ethos">ethos</a></li>
</ul>
</nav>
</div>
<h1>Go Project Layouts</h1>
<p>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…</p>
<p>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.</p>
<p>A lot of design tells you to think about your project in layers.</p>
<ul>
<li>api</li>
<li>domain</li>
<li>storage</li>
</ul>
<p>If you read <a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">The Clean Architecture</a>
you get told the layers should be,</p>
<ol>
<li>entities</li>
<li>use cases</li>
<li>interface adapters</li>
<li>frameworks and drivers.</li>
</ol>
<p>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).</p>
<p>This is an excellent idea; separation of concerns is good.</p>
<p>So you make your folders.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── drivers
</span></span><span class="line"><span class="cl">├── entities
</span></span><span class="line"><span class="cl">├── interfaces
</span></span><span class="line"><span class="cl">└── usecases
</span></span></code></pre>
<p>aaand this is an awful idea. I don’t even want to go further into this hypothetical layout because it hurts too much.</p>
<p>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.</p>
<p>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.</p>
<p>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 <code>domain/bar</code> or <code>domain/foo/bar</code>. Either will simply be accessed as <code>bar</code>. This means that what matters what’s in that package <code>bar</code>. Since everything will be read as <code>bar.Thing</code> i.e <code>import bytes</code> and <code>bytes.Buffer</code>.</p>
<p>So, the package name sets context and expectations. If I grab the <code>json</code> package, I expect that package to do things around <code>json</code>. I’d feel a bit confused if I was able to configure an smtp server.</p>
<p>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</p>
<p>“but you’ve still not provided a good example?”
well
yes</p>
<p>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.</p>
<p>Start with a <code>main.go</code> and make a <code>Run</code> function or some equivalent which it calls.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Run</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// actual important stuff here
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Run</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p>This allows you to test your run function in a unit test, and keeps your <code>main</code> func minimal.</p>
<p>As your project grows, you can keep it flat inside the root directory</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl">├── api.go
</span></span><span class="line"><span class="cl">├── go.mod
</span></span><span class="line"><span class="cl">├── go.sum
</span></span><span class="line"><span class="cl">├── main.go
</span></span><span class="line"><span class="cl">├── rss.go
</span></span><span class="line"><span class="cl">└── sqlite.go
</span></span></code></pre>
<p>Even just glancing at that, you can guess that this might be an RSS server, that uses sqlite to back it.</p>
<p>Who knows what</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl">├── drivers
</span></span><span class="line"><span class="cl">├── entities
</span></span><span class="line"><span class="cl">├── interfaces
</span></span><span class="line"><span class="cl">└── usecases
</span></span></code></pre>
<p>does.</p>
<p>As things evolve you might want to put them in <code>internal</code> to hide them from being imported by other packages, or <code>cmd</code> as you develop multiple binaries. Placing things in <code>internal</code> 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.
<a href="https://go.dev/doc/modules/layout#server-project">Server Project</a></p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl">project-root-directory/
</span></span><span class="line"><span class="cl"> go.mod
</span></span><span class="line"><span class="cl"> internal/
</span></span><span class="line"><span class="cl"> auth/
</span></span><span class="line"><span class="cl"> ...
</span></span><span class="line"><span class="cl"> metrics/
</span></span><span class="line"><span class="cl"> ...
</span></span><span class="line"><span class="cl"> model/
</span></span><span class="line"><span class="cl"> ...
</span></span><span class="line"><span class="cl"> cmd/
</span></span><span class="line"><span class="cl"> api-server/
</span></span><span class="line"><span class="cl"> main.go
</span></span><span class="line"><span class="cl"> metrics-analyzer/
</span></span><span class="line"><span class="cl"> main.go
</span></span><span class="line"><span class="cl"> ...
</span></span><span class="line"><span class="cl"> ... the project<span class="err">'</span>s other directories with non-Go code
</span></span></code></pre>
<p>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.</p>
<footer>
<br />
<hr />
<br />
<p>see something you disagree with? email: <a href="mailto:[email protected]">[email protected]</a></p>
</footer>
</main>
</body>
</html>
|