Why Global Variables are Bad in Go and Alternatives

Repository pattern to avoid global variables
Nov 10 2020 · 3 min read

Introduction 

Using global variables without any strong reason in any programming language is not a good idea.

Why should we not use global variables in Golang?

Global variables have no access control. We can access and change these variables in any part of the code. As the project grows, it becomes difficult to remember the actual reason for every possible use. Any function can access global variables. Hence, it is difficult to debug and understand the existing business logic.

The following tutorial will show us how to avoid using global variables in golang.

Let's take a tour of it!

Create a file that uses a global variable

Take an example of database configuration using sqlx in golang.

package database

import (
   _ "github.com/go-sql-driver/mysql"
   "github.com/jmoiron/sqlx"
)

var Db *sqlx.DB

func SqlConfig() *sqlx.DB {
   host := "localhost"
   port := "3306"
   username := "root"
   password := "secret"
   dbName := "db_name"

   Db = sqlx.MustConnect("mysql", username+":"+password+"@tcp("+host+":"+port+")/"+dbName)

   return Db
}

Here we have declared Db as a global variable and we can access it anywhere in the project.

Create a function that uses the global variable

type User struct {
   id int
   name string
}

func GetUser(id int) (*User, error) {

   var user User
   err := database.Db.Get(&user, "SELECT id, name FROM user WHERE id = ? ", id )
   if err != nil {
      return nil, err
   }
   return &user, nil
}

The function uses a package of global sqlx.DB, to make a query against some database. This operation has side effects that are completely invisible from an inspection of the function signature.

The caller can not predict what database configuration is used for it, except by inspecting the function and digging the definition of the globals.

There are many solutions to avoid this like passing the Db variable in the function signature like this.

func GetUser(Db *sqlx.DB, id int)

I guess for large projects, it’s not a feasible solution. I have found one possible solution and that is making use of repositories.

Understanding repository

Now we will see how to implement repositories to effectively use variables in the project without making it global.

Let’s declare the Db variable inside the function definition.

package database

import (
   _ "github.com/go-sql-driver/mysql"
   "github.com/jmoiron/sqlx"
)

func SqlConfig() *sqlx.DB {
   var Db *sqlx.DB
   host := "localhost"
   port := "3306"
   username := "root"
   password := "secret"
   dbName := "db_name"

   Db = sqlx.MustConnect("mysql", username+":"+password+"@tcp("+host+":"+port+")/"+dbName)

   return Db
}

Now the question is, How to access Db in the entire project?

Here we go...

We will create a struct and repository interface in user/user.go, that will hold the the database dependency for us.

type Repo struct{
   Db *sqlx.DB
}

func RepoInterface(db *sqlx.DB) *Repo {
   return &Repo{Db: db}
}

Let’s add the GetUser() method to this repository

func (repo *Repo) GetUser(id int) (*User, error) {

   var user User
   err := repo.Db.Get(&user, "SELECT id, name FROM user WHERE id = ? ", id )
   if err != nil {
      return nil, err
   }
   return &user, nil
}

As GetUser function is part of the repository, it will have DB variable access that it can use to execute queries.

Now let’s see how we can initialize the repository from the app entry point.

Add a function SqlConfig() in main.go that can provide us database connection like this.

func InitDb() *sqlx.DB {
   db := database.SqlConfig()
   return db
}

In the main function, we will initialize the repo by passing db.

func main() {

   db := InitDb()

   userRepo := users.RepoInterface(db)

   user, err := userRepo.GetUser(<userId>)

   if err != nil {
      log.Println(err)
   }

   fmt.Println(user)
}

Conclusion

That’s it, we are done!!

I took the SQL database just for example purposes. You can map any other database or dependencies like Redis, MongoDB, struct(if required), etc. in repositories depending on your use cases.

We’re Grateful to have you with us on this journey!

Suggestions and feedback are more than welcome! 

Please reach us at Canopas Twitter handle @canopas_eng with your content or feedback. Your input enriches our content and fuels our motivation to create more valuable and informative articles for you.

Similar Articles


sumita-k image
Sumita Kevat
Sumita is an experienced software developer with 5+ years in web development. Proficient in front-end and back-end technologies for creating scalable and efficient web applications. Passionate about staying current with emerging technologies to deliver.


sumita-k image
Sumita Kevat
Sumita is an experienced software developer with 5+ years in web development. Proficient in front-end and back-end technologies for creating scalable and efficient web applications. Passionate about staying current with emerging technologies to deliver.

canopas-logo
We build products that customers can't help but love!
Get in touch
background-image

Get started today

Let's build the next
big thing!

Let's improve your business's digital strategy and implement robust mobile apps to achieve your business objectives. Schedule Your Free Consultation Now.

Get Free Consultation
footer
Subscribe Here!
Follow us on
2024 Canopas Software LLP. All rights reserved.