Setting up a web application in Golang can be a daunting task. If you’re used to rails, you probably miss having your hand held at this point. I’m using this architecture on Refer Madness [source]. Here’s my directory structure:
I’ll break this down architecturally. At each directory level, files are only allowed to access code from directories at the same or a higher level. This means that utils can access itself and models; web can access itself, controllers, utils, and models; and models can only access itself. I do this to keep a thick line between all the layers and to avoid recursive dependencies. Let’s talk about each level individually!
main.go
main.go is my main file in charge of creating dependencies, discovering environment variables, and starting the web server. Here’s a sample:
Since main.go is on the lowest level, it has access to every other directory: in this case, utils and web. I grab all the environment variables and inject them where necessary. I create the server, injecting dependencies, and then start it on the port I want.
web
I keep my server code in the web directory, which includes some web middleware. Here’s the internal structure of the web directory:
main.go
123
-web/|-middleware/|-server.go
server.go contains the definition of my web server. The web server creates all my web controllers and adds middleware to my negroni server. Here’s an excerpt:
A server struct is a negroni.Negroni web server. I create some dependencies from utils and external packages, create a router, create controllers with the dependencies, and register the router with my controllers. I also add in any middleware I may need. Speaking of middleware, here’s an example:
This is a standard middleware definition for accessing a database session from an HTTP route. The base of this file was taken from Brian Gesiak’s blog post on RESTful Go and was modifiedto fit my needs.
controllers/
A controller is similar to the controller concept found in Rails. A controller registers its own route with the router provided and then sets up function calls. An abbreviated example:
Files that belong in the utils directory provide small levels of functionality to support the rest of the codebase. In my case, I have structs which access the request context, manipulate the session, and define a base page for specific pages to inherit. Here’s where I access the context to set the user:
Models are data structures for representing my database concepts. I use my models to access the database - I create an empty model and then populate it with a database query. Here’s an example of a model I use:
I keep my goalng templates in the views directory. Pretty basic - whatever templating engine I use, I throw it into this views directory.
public/
As usual, this directory is all my public files - css, img, scripts.
That’s cool - how do I run this?
It’s no secret that I like docker, so that’s my preferred method of running this application. If I wanted to run this without docker, I would throw the root of this directory into $GOPATH/src/github.com/larryprice/refermadness. Running go get will fetch all the missing dependencies and running go run main.go or go build; ./refermadness should run the file. If you do want to use docker (and you know I do), I start with a Dockerfile:
Dockerfile
1234567
FROM golang:1.4
RUN go get github.com/codegangsta/gin
ADD . /go/src/github.com/larryprice/refermadness
WORKDIR /go/src/github.com/larryprice/refermadness
RUN go get
And, since I also love compose, I run everything with a compose file. I use a JSC transformer, a SASS transpiler, and a mongo database, so my docker-compose.yml file looks like:
And then run docker-compose up to run all the containers and start up the server.
…And that’s that! I like setting my code up this way as a compromise between idiomatic Go and ruby on rails. I inject parameters whrever possible to keep things testable. I keep my concerns separated as much as possible. Go is fast and the syntax is clean, but there aren’t enough examples of creating full-blown web applications available. At this point, we don’t know the future of Go, but I anticipate it becoming big. Hopefully this architecture overview can help someone get started making the transition from a more traditional MVC framework to golang.