GopherCon 2018 – How do you Structure your Go Apps?
GopherCon 2018 was held in the beautiful city of Denver, not to mention I had an awesome mountain view from my hotel room. Here is the proof:
I had no prior knowledge of Golang, so I decided to attend a day-long pre-conference workshop for Go Beginners lead by GoBridge, Johnny Boursiquot and team. This was a full hands-on workshop with lots of exercises to implement what we learned. The workshops also had TAs available throughout the day to help people with any questions they had.
Here are some of the topics covered in the workshop:
- Work with variables and constants.
- Work with lists and looping
- Work with condition if/else statements
- Use functions for reusing functionality
- Writing tests for the functions
- Write command line applications
- Use interfaces to write more flexible programs
- Write a simple concurrent program
In short, the workshop teaches you the fundamental programming techniques, so you can get the most out of the talks you attend at the conference. Oh, and everyone kept talking about concurrency in Golang which I didn’t know much about. For anyone interested, here is a very clear and straightforward article that talks about very basic concurrency for beginners in Go. I really enjoyed the workshop and would highly recommend anyone who has no prior Go knowledge.
There were many interesting talks but one of the talks that I found interesting at the conference as a beginner was “How do you Structure your Go Apps?” by Kat Zien. Kat tires to address some of the common questions asked by programmers like should we keep all our files in one directory or split them up? How much should be shared between packages? Why some projects have cmd directory and what is the advantage of that? Should I just use framework? Should I go with Microservices or monolith?
Kat stresses to have a consistent structure that is simple, easy to understand, navigate and test. The structure of your code should reflect how your software works. The code structure should work with you and for you, not against you. Here is the link to her go-structure-examples
Types of structures
- Flat Structure:
- If you don’t know where to start, then go with the flat structure and don’t over complicate if you don’t need to.
- Flat structure works well for small apps and libraries. Benefit of flat structure is that it removes any chance of circular dependencies because everything is in the main package.
- It also has some disadvantages because you cannot separate things out and everything can be accessed and modified by everything and looking at this structure tells nothing about the app.
- Group by function (Layered Structure):
- This approach is a classic MVC used by a lot of frameworks which places relevant files in the same package.
- Its easy to decide what goes where until certain extent and discourages you to use global state.
- This structure also has a small problem, for example if you have a variable shared between the layers then which layer do you put it in?
- Another problem is where do you initialize everything? Does main initialize the storage or does each model initialize its own storage?
- Everything to do with beers goes in one package, everything to do with review goes in its own package and everything to do with storage goes in its own package.
- Even though everything is grouped, it still can get confusing if the reviews should go in the beers package since they are beer reviews, or should they have their own package. You can also run into funny naming clashes here.
- Domain Driven Development (DDD):
- DDD works by establishing your domain and business logic before you start writing any code.
- Define your bounded context, it will help you decide what must stay consistent and what can change independently.
- Categorize the building block of your system:
- As a result we get a new structure: Group by Context:
- Hexagonal Architecture:
- This architecture helps you achieve the goal of being able to easily change one part of the app without having to change the other part.
- Here dependencies are only allowed to point inwards, so the outer layers can reach out to the domain as much as they like but the domain can not reference anything outside of it, which means heavy use of interfaces and Inversion of Control.
- Here is the final architecture:
- The packages in red are input and output interfaces.
- The goal here is to separate the binaries from your actual Go code, so if you have any binary then you put them under the CMD package.
- Here the main function is broken into two parts, so they are completely independent, and you can run one without running the other.
- Putting it all together in Go:
- There is no single right answer, but it was good to know all the different approaches you can use depending on your needs.
- Make it as simple as possible but not simpler
- Maintain consistency throughout the app.
- Experiment and share ideas.
This was my first time attending a conference after joining Enova and overall it was a great learning experience and I would most certainly recommend it to anyone interested in Go. A big thank you to Enova for sponsoring the entire trip to the conference.