Building Microservices in Go : Part 3 (Database, Models, Migrations)

Saad Farhan
5 min readNov 9, 2022

--

In Part 1 we laid down the basic foundation of our micro-service and in Part 2 we configured live reloading and added a database service which our services could interact with. In this part we are going to connect our micro-service with the database, learn how to define models and run migrations. This is what we all were waiting for so let’s get right into it

We are going to start our services by running in the terminal

docker-compose up

If we open our docker desktop then we will see something similar to this:

We’ve got both of our services up and running. Now it’s time to enable communication between them. Let’s create a new folder “database” inside our “web_blog” folder and then create a “database.go” file inside it. Your directory structure should look like this :

Now we are going to change into our web_blog directory and add in some dependencies :

cd services/web_bloggo get -u github.com/beego/beego/v2
go get -u github.com/beego/bee/v2
go get github.com/lib/pq
go get github.com/beego/beego/v2/client/orm

This will add beego into our project and we are going to use its ORM layer. You can find the docs for beego here. Since we’ve added in all the required dependencies let’s open database.go and start writing some code

We are going to start by declaring our package and then importing the required modules

package databaseimport (
"database/sql"
"log"
"github.com/beego/beego/v2/client/orm"
_ "github.com/lib/pq"
)

Next we are going to define a struct that we can use for our database connectivity, an instance of this struct and function that we will use to connect to the database.

Note : Notice that both the function and the variable starts with uppercase letters. This is because we need these to be publicly accessible.

type DBInstance struct {
Db *sql.DB
}
var Database DBInstancefunc ConnectDB() {
}

Now we are going to start writing the implementation for ConnectDB(). If I breakdown it into steps then we can say that we need to :

  1. Register our database driver.
  2. Register our database.
  3. Open our database connection.

This might sound complicated but it’s actually quite simple. Let’s see how we can do this in code

We start by creating our database connection string which has the following format :

postgresql://USER:PASSWORD@HOST:PORT/NAME?sslmode=disable

So according to this format we have the following values :

  1. User = postgres
  2. Password = postgres
  3. Host = db (our database docker container)
  4. Port = 5432
  5. Name = web_microservices (database name)

Next, on line 18, we register our driver by passing it the driver name and type. We proceed by registering our database on line 21 by specifying our database alias (default), driver name and datasource which is our database url. We check for errors and if there are none then we can move to our step number 3 which is to open our database connection. We do this on line 25 by using the open method and again we check for errors and if there are none, we initialize our DBInstance variable by assigning it our database connection.

Let’s import this into our main.go file and call the ConnectDB() function there.

...
import (
...
...
"github.com/saadfarhan124/microservices_go/web_blog.git/database"
)
func main(){
...
database.ConnectDB()
log.Fatal(app.Listen(":3069"))
}

If everything goes well we should be able to see this in the console

So now that our connection is up and running, we are going to define our models.

Let’s create another folder inside our “web_blog directory” called models and inside it create another file “models.go”. Your directory structure should look like this

For now we are going to define a simple Author model that will have an auto incremented primary key id, a title, created at and updated at fields. Lets see how to do this

Just like with database.go we are going to start by declaring our package name and imports

package modelsimport "time"

Time to define our model

type Authors struct {Id        int       `json:"id"`
Title string `json:"title"`
CreatedAt time.Time `orm:"auto_now_add;type(datetime)" json:"created_at"`
UpdatedAt time.Time `orm:"auto_now;type(datetime)" json:"updated_at"`
}

We basically define two types of attributes for each field json and orm. In orm we can define all the database related fields, like if a column should be nullable, if it should have a default value etc. Json basically describe the struct’s behaviour when its converted to json form so we are just defining the json key for each struct. Id field of any struct is by default recognized by Beego as an auto incremented primary key.

Our complete models.go should look like this

Now that we have defined our model we are going to register it in our database. We are going to add this line in main.go right before we call ConnectDB() function.

orm.RegisterModel(new(models.Authors))

We also need to add the required imports

"github.com/saadfarhan124/microservices_go/web_blog.git/models"

main.go should look like this now

If we hit save, we still aren’t able to see any changes on the console. That’s because we need to update our database.go to make it sync our model changes to the database by adding the following snippet

if err := orm.RunSyncdb("default", false, true); err != nil {
log.Fatal(err.Error())
} else {
Database = DBInstance{Db: db}
log.Print("Connected Succcessfully")
}

Our database.go should look like this :

As soon as we hit save we will be able to see this in our console :

And if we go to pgAdmin, locate our server and expand schema we should be able to see our author table as well

We are going to end the tutorial here since this one has gotten very long. In the next part we are going to create CRUD endpoints for our model, track changes and configure .env files. You can find the source code for this tutorial here.

--

--

Saad Farhan

Product Engineer / Flutter / Gopher / Python / Movie Geek