Golang is quirky

So recently I had the opportunity to make a proof of concept (PoC) in the Go language to share with some fellow developers. They are mostly Java developers and therefore I decided to make the PoC a MVC style REST API that returns JSON with a single Entity for now.

It does however store everything in a Postgresql database and features the full suite of HTTP verbs; GET, POST, PUT, PATCH and DELETE. With GET both returning the single entity and the list of all entities. I did not make pagination and transactions in the database part as it was not necessary for this PoC.

Syntax

The syntax still is quirky but now I know more of why it is the way it is. Everything that is Pascal case (meaning uppercase letters on the first words, ChocolateChipCookie for instance) will be public to other parts of the code. Everything that is camel cased (meaning the first letter is lowercase so chocolateChipCookie) will not be public to other parts of the code that exist in other packages. This explains a lot, as this way of making the formatting determine the accessibility levels shapes the way the code looks. This goes for methods, structs and anything else.

Not only do they need to be named as such, they also have to be foreseen of a comment. This I felt led to slowdown in the development cycle and a lot of redundant stupid comments that just annoyed me. If you name it Pascal case but no comment it will not be exported, which is the vernacular in Go to mean it is a public method. This also means that in a struct that will be used to return a response DTO you have to name everything with Pascal case and then use so called tags to shape the JSON data again.

Formatting

There is an auto formatting tool used on your code and it has to conform to that format otherwise you cannot compile. This is not too bad however if it conflicts with your own style then you have a problem.

Imports

You cannot have unused imports and they will be removed automatically for you. This is quite annoying to say the least. I want to have the module imported in order to see what is inside it. This fights me on so many occasions. So you have to import something make a stupid variable to keep it around and look into it then.

Errors

The error handling sucks. You have two extremes, panic and Errors.New. The panic stops the whole program, kind of like a segfault or Runtime exception in Java. The Errors.New has to bubble up through all the methods, so almost every method will return the data and the error. Then you have to manually check it. There is no problem with the following code:

⋮
data, _ := db.Find(&fruit)
⋮

Which means you ignore the error and carry on which is not what you want maybe. It also means you can inadvertently do this, without realizing it and then get some weird errors in production.

Packages

The packages are directory based only. So you can only have one package declared in one directory. You can have many files in one directory all belonging to the same package but the package is the identifier and not the file name like in Java. This required some getting used to. I also feel you miss some control somehow, as I ran into the fact I wanted to have a entity package but within it you will have all the entity straight by name. There does not exist a nested package where you type in entity.fruit.Fruit for example, then it will just be fruit.Fruit and it will come from the folder entity/fruit/ for example. Under water you can have multiple naming of fruit just coming from different areas. I also inverted the naming, so making fruit carry all the different proponents like service and dao making it easier to deal with it.

Getting started

The whole getting started is getting a go.mod file by executing go mod init <name> and then you are off. The only slight downside is you have to manage your own GOPATH environment variable to make a sort of working virtualenv like Python.

Idiomatic

There is no idiomatic way of writing Golang. There are rules for style and grammar and the like but they do not dictate how you write the overall architecture of the Golang application itself. It is difficult to get consistency across the ecosystem and therefore you get the same problem as in React for example and PHP as well. There will be subgroups and everyone is left to their own devices on how to do things.

That something is considered Pythonic is a good thing. There should optimally be only one clear way to do something. That means across the whole application and across the whole language ecosystem.

Classes and methods

There exists no such thing as a class in the Go language. What you do have are called structs and they can contain methods on them. So you construct the full class and methods and internal and external variables that are accessible by making a struct. A quick example of how to translate a class called Message with a constructor that takes in one argument called msg which is a string would be:

type Message struct {
    Msg string
}

To give that class the method display you would do the following:


func (m *Message) Display() {
    fmt.Println(m.Msg)
}

There exists no such thing as this or self either, so you have to use the named way of adding methods to structs.

Things left to be discovered

The things I yet have to touch on and discover are the goroutines and the channels. These things I will uncover in a future post about CQRS where I will turn the following application into a CQRS one.

The PoC itself

So below is the PoC itself and the structure, there is not a lot going on as it is an easy application. I did try to get as much of the functionality in here of the Go language itself as that was the goal to showcase as much as possible.

├── controllers
│   ├── controllers.go
│   └── fruit
│       └── fruit.go
├── dist
│   └── gin
├── docker-compose.yaml
├── Dockerfile
├── go.mod
├── go.sum
├── main.go
├── models
│   ├── dto
│   │   └── fruit.go
│   ├── entity
│   │   └── fruit.go
│   └── models.go
├── pkg
│   └── mod
│       └── cache
│           └── lock
├── router
│   └── router.go
└── services
    ├── dao
    │   ├── dao.go
    │   └── fruit
    │       └── fruit.go
    ├── fruit
    │   └── fruit.go
    └── services.go

The folders dist and pkg can be ignored for now. We will start with the go.mod one which contains the following.

module pocs/go/gin

go 1.15

require (
    github.com/gin-gonic/gin v1.6.3
    github.com/google/uuid v1.1.1
    github.com/jinzhu/gorm v1.9.16
)

I named it gin too, which is mainly because that is the framework I used to make the web app this time. The last name of the whole thing is what Go will call it. Therefore there is a gin executable in the dist folder.

Then the go.sum is just there for the checksums. The docker-compose.yaml is there to help the devs out a bit more.

version: "3.6"

networks:
    gin_connect:
        name: "gin_connect"

services:
    gin:
        image: golang:1.15-alpine3.12
        container_name: gin.go.local
        env_file:
            - .env/app
        volumes:
          - .:/srv/http
        command: "ash -c 'cd /srv/http && go run --tags=jsoniter .'"
        depends_on:
            - db
        networks:
            - gin_connect
        stdin_open: true
        tty: true
        ports: 
            - 127.0.0.1:9999:8080
    db:
      image: postgres:13-alpine
      container_name: db.go.local
      environment:
        POSTGRES_PASSWORD: go
        POSTGRES_USER: go
        POSTGRES_DB: go
      stdin_open: true
      tty: true
      networks:
        - gin_connect

Then the Dockerfile is there to make the “actual” docker image that will be run in the cloud environments.

FROM golang:1.15-alpine3.12 as builder

COPY . /srv/http

RUN cd /srv/http && go build --tags=jsoniter .

FROM alpine:3.12

COPY --from=builder /srv/http/dist/gin /app

CMD ["/app"]

Actual code

So let us get into the actual code. This is what the main.go file looks like:

package main

import (
    "pocs/go/gin/router"
    "pocs/go/gin/services"
)

func main() {
    r := router.Router()
    services.SetupDatabase()
    r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

Every Go application looks for one function called main in the main package. Packages are directory based so the root of this application is the main package. It just sets up the Router and the database. Also note that main is private as it is lowercased.

The router package is located in router.go.

package router

import (
    "github.com/gin-gonic/gin"
    "pocs/go/gin/controllers"
)

// Router sets up the complete routes from controllers and returns the full router
func Router() *gin.Engine {
    router := gin.Default()
    controllers.Init(router)
    return router
}

I chose to go the route of having the controllers package itself supply the routes by making each controller supply an Init method.

First the database though. It is in services.go.

package services

import (
    "pocs/go/gin/models/entity"
    "pocs/go/gin/services/dao"
)

// SetupDatabase makes sure all schemas are present
func SetupDatabase() {
    db := dao.Database()
    db.AutoMigrate(&entity.Fruit{})
}

It just opens a connection to the database and does AutoMigrate for the one entity we have which makes sure the schema is there. This is a nice feature, but I have no idea how changes are managed yet.

Then the database comes from dao.go.

package dao

import (
    "fmt"
    "os"

    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/postgres" // Blank import for wrapping GORM
)

// Database returns a database connection
func Database() *gorm.DB {
    host, user, pass, dbname := os.Getenv("DB_HOST"), os.Getenv("DB_USER"), os.Getenv("DB_PASS"), os.Getenv("DB_NAME")
    ssl := "disable"
    _, ok := os.LookupEnv("DB_SSL")
    if ok {
        ssl = "require"
    }
    db, err := gorm.Open("postgres", fmt.Sprintf("host=%s port=5432 user=%s dbname=%s password=%s sslmode=%s", host, user, dbname, pass, ssl))
    if err != nil {
        panic(err)
    }
    return db
}

This package just connects to the database. I have no idea yet if gorm.Open gives back a connection pool or that you just open and close and that underwater the open will just have a internal access to a connection pool. I have to delve into that one a bit more*. For now though it is just a PoC so this works for just one entity and no active users to one at most at a time. *Found out the underlying mechanism and indeed one call to Open will return a connection pool.

Models

Now that the database is out the way let us talk about the models. I use one Entity to represent the database records themselves and some DTOs. models.go just contains some empty package declaration. I created this as I was debugging the reason why it could not find my package. It turned out it was how I was calling it in the controller that was causing the issue. This file might be removed altogether.

package models

Then there is fruit.go inside entity.

package entity

import (
    "time"

    "github.com/google/uuid"
)

// Fruit represents the fruit in the database
type Fruit struct {
    ID        uuid.UUID
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt *time.Time
    Name      string
}

It just contains some standard stuff. The reason why DeletedAt is a pointer, is because then it can be nil. Standard GORM only supports integers as ids. This is a bad idea I feel, so therefore it is now a uuid. That does mean some manual action on certain actions, that is okay. Next up are the DTOs inside fruit.go in dto.

package dto

import (
    "pocs/go/gin/models/entity"
    "github.com/google/uuid"
)

// ReadFruit is the DTO for getting fruit at GET /fruit/:id
type ReadFruit struct {
    ID string `uri:"id" binding:"required,uuid"`
}

// CreateFruit is the DTO for creating new fruit at POST /fruit
type CreateFruit struct {
    Name string `json:"name" binding:"required,max=255"`
}

// UpdateFruit is the DTO for creating new fruit at POST /fruit
type UpdateFruit struct {
    Name string `json:"name" binding:"required,max=255"`
}

type metadata struct {
    CreatedAt int64 `json:"created_at" `
    UpdatedAt int64 `json:"updated_at" `
}

// ResponseFruit is the DTO for all responses
type ResponseFruit struct {
    ID       uuid.UUID `json:"id" `
    Name     string    `json:"name" `
    Metadata metadata  `json:"metadata" `
}

// Single creates a single ResponseFruit from a entity
func (res *ResponseFruit) Single(fruit entity.Fruit) *ResponseFruit {
    return &ResponseFruit{ID: fruit.ID,
        Name: fruit.Name,
        Metadata: metadata{
            CreatedAt: fruit.CreatedAt.Unix(),
            UpdatedAt: fruit.UpdatedAt.Unix()}}
}

// Multi creates an array of responses
func (res *ResponseFruit) Multi(fruits ...*entity.Fruit) []*ResponseFruit {
    result := make([]*ResponseFruit, cap(fruits))
    for i := range fruits {
        fruit := fruits[i]
        result[i] = res.Single(*fruit)
    }
    return result
}

This contains the DTO necessary for holding certain bindings in order to utilize gin functions in the controller that will check if you supplied the correct data. These occur in the form of so called tags. These are the string after the type declaration inside the struct. Then I added two helper methods, Single and Multi . They take in a entity and convert them into a ResponseFruit DTO. The ... means a variadic input, so it can be 0,1 or any other whole positive integer.

Controllers

The controllers start with the uniform entry point in controllers.go.

package controllers

import (
    "github.com/gin-gonic/gin"
    "pocs/go/gin/controllers/fruit"
)

// Init takes router and calls all subsequent init methods
func Init(router *gin.Engine) {
    fruit.Init(router)
}

This will be extended on later of course. Then the only one we have right now is fruit.go inside of the controllers.

package fruit

import (
    "net/http"
    "reflect"

    "github.com/gin-gonic/gin"
    "pocs/go/gin/models/dto"
    "pocs/go/gin/services/fruit"
    "github.com/google/uuid"
)

// Init takes router and adds the necessary routes to it
func Init(router *gin.Engine) {
    group := router.Group("/fruit")
    {
        group.GET("/", listHandler)
        group.GET("/:id", getHandler)
        group.POST("/", postHandler)
        group.PATCH("/:id", patchHandler)
        group.PUT("/:id", putHandler)
        group.DELETE("/:id", deleteHandler)
    }
}

var service = &fruit.Service{}

var listHandler = func(c *gin.Context) {
    fruits, err := service.FindAll()
    if err != nil {
        c.SecureJSON(http.StatusNotFound, gin.H{"msg": err.Error()})
        return
    }
    var res = dto.ResponseFruit{}
    c.SecureJSON(http.StatusOK, gin.H{"data": res.Multi(fruits...)})
}

var getHandler = func(c *gin.Context) {
    var req dto.ReadFruit
    if err := c.ShouldBindUri(&req); err != nil {
        c.SecureJSON(http.StatusBadRequest, gin.H{"msg": err.Error()})
        return
    }
    fruit, err := service.Find(uuid.MustParse(req.ID))
    if err != nil {
        c.SecureJSON(http.StatusNotFound, gin.H{"msg": err.Error()})
        return
    }
    var res = dto.ResponseFruit{}
    c.SecureJSON(http.StatusOK, gin.H{"data": res.Single(*fruit)})
}

var postHandler = func(c *gin.Context) {
    var req dto.CreateFruit
    if err := c.ShouldBindJSON(&req); err != nil {
        c.SecureJSON(http.StatusBadRequest, gin.H{"msg": err.Error()})
        return
    }
    fruit, err := service.Create(req.Name)
    if err != nil {
        c.SecureJSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
        return
    }
    var res = dto.ResponseFruit{}
    c.SecureJSON(http.StatusOK, gin.H{"data": res.Single(*fruit)})
}

var patchHandler = func(c *gin.Context) {
    var uriCheck dto.ReadFruit
    var reqBodyCheck dto.UpdateFruit
    if err := c.ShouldBindUri(&uriCheck); err != nil {
        c.SecureJSON(http.StatusBadRequest, gin.H{"msg": err.Error()})
        return
    }
    if err := c.ShouldBindJSON(&reqBodyCheck); err != nil {
        c.SecureJSON(http.StatusBadRequest, gin.H{"msg": err.Error()})
        return
    }
    obj, err := service.Find(uuid.MustParse(uriCheck.ID))
    if err != nil {
        c.SecureJSON(http.StatusNotFound, gin.H{"msg": err.Error()})
        return
    }
    elem := reflect.ValueOf(&reqBodyCheck).Elem()
    typeOf := elem.Type()
    for i := 0; i < elem.NumField(); i++ {
        f := elem.Field(i)
        v := reflect.ValueOf(obj).Elem().FieldByName(typeOf.Field(i).Name)
        if v.IsValid() {
            v.Set(f)
        }

    }

    fruit, err := service.Update(*obj)
    if err != nil {
        c.SecureJSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
        return
    }
    var res = dto.ResponseFruit{}
    c.SecureJSON(http.StatusOK, gin.H{"data": res.Single(*fruit)})
}

var putHandler = patchHandler

var deleteHandler = func(c *gin.Context) {
    var req dto.ReadFruit
    if err := c.ShouldBindUri(&req); err != nil {
        c.SecureJSON(http.StatusBadRequest, gin.H{"msg": err.Error()})
        return
    }
    _, err := service.Delete(uuid.MustParse(req.ID))
    if err != nil {
        c.SecureJSON(http.StatusNotFound, gin.H{"msg": err.Error()})
        return
    }
    c.SecureJSON(http.StatusNoContent, nil)
}

There is a lot of repetition. So for the future I would definitely want to utilize some compression oriented programming and take those out into more private methods.

Services

Then we get to the service in fruit.go inside services/fruit.

package fruit

import (
	"pocs/go/gin/models/entity"
	"pocs/go/gin/services/dao/fruit"
	"github.com/google/uuid"
)

// Service is there to hold everything and have multiple copies
type Service struct {
}

var dao = &fruit.DAO{}

// Find gives back the Fruit with given uuid if it exists
func (s *Service) Find(id uuid.UUID) (*entity.Fruit, error) {
	return dao.FindByID(id)
}

// FindAll gives back the Fruit with given uuid if it exists
func (s *Service) FindAll() ([]*entity.Fruit, error) {
	return dao.Find()
}

// Create will assign random uuid and set createdAt date
func (s *Service) Create(name string) (*entity.Fruit, error) {
	fruit := entity.Fruit{Name: name, ID: uuid.New()}

	return dao.Insert(fruit)
}

// Update updates the resource if it can find it
func (s *Service) Update(fruit entity.Fruit) (*entity.Fruit, error) {

	return dao.Update(fruit)
}

// Delete soft deletes the record
func (s *Service) Delete(id uuid.UUID) (*entity.Fruit, error) {
	var fruit = entity.Fruit{}
	fruit.ID = id
	return dao.Delete(fruit)
}

Just a bunch of helper methods for doing CRUD. Then I also made a DAO layer just to help out the Java devs a bit more. It is inside fruit.go in services/dao/fruit.

package fruit

import (
	"fmt"

	"pocs/go/gin/models/entity"
	"pocs/go/gin/services/dao"
	"github.com/google/uuid"
)

// DAO struct that will hold the methods for doing DAO stuff
type DAO struct {
}

var db = dao.Database()

// Find gets all fruits without pagination
func (DAO) Find() ([]*entity.Fruit, error) {
	var fruits []*entity.Fruit
	result := db.Find(&fruits)
	if result.Error != nil {
		return nil, result.Error
	}
	return fruits, nil
}

// FindByID helper method to call internal find method
func (DAO) FindByID(id uuid.UUID) (*entity.Fruit, error) {
	fruit := entity.Fruit{}
	result := db.Where("id = ?", id.String()).First(&fruit)
	if result.RecordNotFound() {
		return nil, fmt.Errorf("Fruit with id: %s does not exist", id)
	}
	return &fruit, nil
}

// Insert will insert record
func (d *DAO) Insert(fruit entity.Fruit) (*entity.Fruit, error) {
	result := db.Create(&fruit)
	if result.Error != nil {
		return nil, result.Error
	}
	return d.FindByID(fruit.ID)
}

// Update will update the record in the database
func (d *DAO) Update(fruit entity.Fruit) (*entity.Fruit, error) {

	result := db.Save(&fruit)
	if result.Error != nil {
		return nil, result.Error
	}

	return d.FindByID(fruit.ID)
}

// Delete will soft delete the record in the database
func (d *DAO) Delete(fruit entity.Fruit) (*entity.Fruit, error) {

	_, err := d.FindByID(fruit.ID)
	if err != nil {
		return nil, err
	}
	result := db.Delete(&fruit)
	if result.Error != nil {
		return nil, result.Error
	}
	db.Unscoped().Find(&fruit)

	return &fruit, nil
}

The reason the extra FindByID calls occur are because the Save and Create methods from GORM do not update the actual representation sent in, that only occurs with the Find method. By default Find will not look for soft deleted records, therefore the Delete uses the Unscoped variant. The Service and DAO layers are consistent in that they all return the entity but I just do not do anything with it in the controller for the DELETE verb.

Conclusions

That is it for the PoC. Just a simple application that hopefully showcases the Go language. It is quirky at times but you get used to it and also there exist enough good libraries out there that you can use to make web application dev possible. I do think it is best used for the things it is being used for right now, which is to make CLI tools that will run everywhere. Like Docker and Kubernetes as well as a webserver called Caddy that can run anywhere.

The thing that I dislike the most is the error handling, or lack thereof. It feels very janky that you have to manually check and bubble errors all over the place. Almost every method returns the error again. This means it is quite cumbersome and you keep writing the same code over and over again.

Also the structure of the Go application is a bit arbitrary, so you can go all out like I did here or just put it all together and keep things as private as possible everywhere. I think this is just all preference and you should make good agreements within the team on what will be the way forward.

#code #golang