Idiomatic Go Web Application Structure

August 1, 2018

There are a lot of posts out there that talk about how to structure your Go web application to be more idiomatic. You see when Go started gaining popularity, and a lot of Ruby/Node/Java developers started using it, the type of structure you would find in these apps was very similar to what you would find in other languages. Most people would set the structure like this:

main.go
controllers/
models/
views/
utils/

A lot of these developers were taught about MVC early on and frameworks like Ruby on Rails (which is great) enforced this type of structure or convention. While the above is not horrible (trust me you can ship with it), it is not considered idiomatic Go….also depends who you ask.

A more idiomatic structure would be:

main.go
model/
  account.go
services/
  account/
    handler.go
    service.go

Your handler should solely be used for HTTP request parsing and serializing, while the service object should contain the raw business logic. Your service shouldn’t really need to know about HTTP or how it’s being called, this will allow for better reusability and most importantly, make it a whole lot easier to test!

Here is an example handler:

type Handler struct {
  Service Service // <-- Account service
}

func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
  body, err := ioutil.ReadAll(r.Body)
  if err != nil {
    // handle error
  }
  account := model.Account{}
  if err := json.Unmarshal(body, &account); err != nil {
    // handle error
  }
  if err := h.Service.Create(&account); err != nil {
    // handle error
  }
  w.WriterHeader(http.StatusOK)
}

Here is an example service:

type Service struct {}

func (s *Service) Create(account *model.Account) error {
  return nil
}

Notice that the service has no interface, it is a simple struct and an implementation of its methods, it also does not concern itself with the method of transport (HTTP). Which brings me to the next point below.

Another common pattern you would find a lot in these applications, was the Java-esque style of writing services. Go has a fantastic interface implementation that allows you to define these implicit contracts of behavior to be required by something else. Unfortunately, in the wild it has taken a few cycles for the community to use them to their maximum potential. I myself started using them wrong as I was taught with traditional Java/OOP.

Here is a sample service interface you will see a lot:

type AccountService interface {
  Get(userID string) (*model.User, error)
  Create(user *model.User) error
  Update(user *model.User) error
  Delete(userID string) error
}

type accountService struct {}

func (a *accountService) Get(userID string) (*model.User, error) {
  return nil, nil
}

...

The above defines an interface for the whole service, as you would imagine these interfaces can get quite big with a lot of behavior attached them. Then you have a backing struct that actually implements the interface.

In other parts of the code, you’ll find references to the AccountService interface, for example:

func checkUserActive(as AccountService, userID string) (bool, error) {
  user, err := as.Get(userID)
  if err != nil {
    return false, err
  }

  return (user.Active && !user.Deleted), nil
}

The code above works, and it compiles but it is not the best code you can write. If you ever needed to pass in a different account service or even unit test the function above, you would have to create an object that satisfy a whole set of behavior that checkUserActive doesn’t even use. Rob Pike has said publicly that the bigger the interface, the weaker the abstraction and this could not be more true. Instead of defining fat interfaces for your services, define none at all. Whatever callers depend on that service, can define a smaller contract and set of behavior that it actually needs.

Here is how the example aboves could be more idiomatic:

type AccountService struct {}

func (a *AccountService) Get(userID string) (*model.User, error) {
  return nil, nil
}

...

type UserGetter interface {
  Get(userID string) (model.User, error)
}

func checkUserActive(as UserGetter, userID string) (bool, error) {
  user, err := as.Get(userID)
  if err != nil {
    return false, err
  }

  return (user.Active && !user.Deleted), nil
}


...

func main() {
  as := AccountService{}
  checkUserActive(as, "1234")
}

This code will compile and has a thin level of abstraction that will serve you well when it comes time to refactor or add more functionality. Notice we don’t define a service interface, instead we write our service as is, and the checkUserActive function simply defines a contract of what type of object must be passed in to satisfy the implementation.

I learned a lot about my Go code when I started implementing these idioms and I hope it helps you as well.

Another great article that summarizes this and more is: https://commandercoriander.net/blog/2018/03/30/go-interfaces/