# Effective Project Structure for Backend Projects in Go

## Introduction

This article provides an optimal and modular project structure for backend projects in Golang, which I use as a starting template for my projects to ensure they are scalable and well maintained.

Along with the structure, it also includes a starter codebase built using `gin` framework, which can be used as a template.

## Why Project Structure Matters?

Designing the structure of a project is important right from the beginning of the development. As the project grows, so does the codebase & complexity, which needs to be distributed properly across well-thought folders and files.

A clear structure makes it easy for the developers to navigate through the code as it grows and also lets future developers focus on implementing new features rather than spending time searching for relevant code.

## Folder Structure

![folder structure](https://cdn.hashnode.com/res/hashnode/image/upload/v1750654020754/aaf0d38c-365f-4f2d-a3a3-efe17b13c763.png align="center")

### `/api`

The `api` folder consists of sub-folders to maintain the versioning of API to manage changes without breaking existing clients using previous versions.

```plaintext
├── api/
│   └── v1/
│       └── routes.go 
│       └── userRoutes.go
```

The sub-folders for versions named as `v1`, `v2`, etc contains `routes.go` files which basically registers different routes divided into different files based on entity/features.

```go
// routes.go
package v1

import (
	"database/sql"

	"github.com/gin-gonic/gin"
)

func RegisterRoutes(router *gin.Engine, db *sql.DB) {
	registerUserRoutes(router, db)
}
```

```go
// userRoutes.go
package v1

import (
	"database/sql"

	"github.com/Aniketyadav44/go-backend-template/internal/handlers"
	"github.com/Aniketyadav44/go-backend-template/internal/services"
	"github.com/gin-gonic/gin"
)

func registerUserRoutes(router *gin.Engine, db *sql.DB) {
	userService := services.NewUserService(db)
	userHandler := handlers.NewUserHandler(userService)

	v1 := router.Group("/api/v1")
	{
		v1.GET("/users", userHandler.GetAllUsers)
	}
}
```

Here, we basically created a `service` and `handler` instance by first injecting the database dependency in the service and then passing this service to the handler.

Then we defined a route group for `/api/v1` path named as `v1` and on this, we register our endpoint methods using the respective handler functions.

### `/cmd`

The `cmd` folder is used as the entry point of our backend application, which contains sub-folders depending on the type of application we are building.

e.g `cmd/api` for our REST API or `cmd/grpc` for a grpc service or `cmd/cli` for an CLI application.

These sub-folders have their `main.go` file which is the exact entry point file of the project.

```plaintext
├── cmd/
│   └── api/
│       └── main.go
```

```go
// main.go
package main

import (
	"log"

	v1 "github.com/Aniketyadav44/go-backend-template/api/v1"
	"github.com/Aniketyadav44/go-backend-template/internal/config"
	"github.com/gin-gonic/gin"
)

func main() {
	cfg, err := config.LoadConfig()
	if err != nil {
		log.Fatal("error in loading config: ", err)
	}

	router := gin.Default()
	v1.RegisterRoutes(router, cfg.DB)

	if err := router.Run(":" + cfg.Port); err != nil {
		log.Fatal("error in starting server: ", err)
	}
}
```

Here, we first load our config instance which provides the port and database instance for this demo.

Next we create a Gin router instance and then we register our API routes from the `v1` package in `api/v1` on this router.

Finally, we start the server using `router.Run()` binding it to the port we loaded from `.env` in config.

### `/internal`

the `internal` folder consists of the application level logic further divided into sub folders depending on the configuration, control(presentation), business logic, data models and middlewares.

```plaintext
├── internal/
│   ├── config/
│   │   └── config.go
│   │   └── db.go              
│   ├── handlers/
│   │   └── userHandler.go              
│   ├── services/
│   │   └── userService.go              
│   ├── models/
│   │   └── userModel.go               
│   └── middleware/
│       └── authMiddleware.go
```

### `/internal/config`

the `config` sub-folder in `internal` folder contains files for the `config.go` file which loads the environment variables and initializes our database instance.

```go
// config.go
package config

import (
	"database/sql"
	"os"

	"github.com/joho/godotenv"
	_ "github.com/lib/pq"
)

type Config struct {
	Port string
	DB   *sql.DB
}

func LoadConfig() (*Config, error) {
	if err := godotenv.Load(); err != nil {
		return nil, err
	}

	port := getEnvKey("PORT", "")
	dbHost := getEnvKey("DB_HOST", "")
	dbPort := getEnvKey("DB_PORT", "")
	dbUser := getEnvKey("DB_USERNAME", "")
	dbPassword := getEnvKey("DB_PASSWORD", "")
	dbName := getEnvKey("DB_NAME", "")

	db, err := loadDb(dbHost, dbPort, dbUser, dbPassword, dbName)
	if err != nil {
		return nil, err
	}

	return &Config{
		Port: port,
		DB:   db,
	}, nil
}

func getEnvKey(key, defaultValue string) string {
	if val, exists := os.LookupEnv(key); exists {
		return val
	}
	return defaultValue
}
```

In this, we have defined a `Config` struct which holds the application level configurations. Specifically in this demo template, there is our database connection instance(`*sql.DB`) and server port.

Then, there is a function `LoadConfig()` which returns the loaded `Config` along with any error. This function first loads the environment using `godotenv` package and then loads database connection using `loadDb()` function defined in `/config/db.go` file.

In this `db.go` file, we create our database connection instance using `sql` package and test our connection using the `db.Ping()` function

```go
// db.go
package config

import (
	"database/sql"
	"fmt"
)

func loadDb(dbHost, dbPort, dbUser, dbPassword, dbName string) (*sql.DB, error) {
	psConnStr := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable", dbUser, dbPassword, dbHost, dbPort, dbName)

	db, err := sql.Open("postgres", psConnStr)
	if err != nil {
		return nil, err
	}

	if err := db.Ping(); err != nil {
		return nil, err
	}

	return db, nil
}
```

Just like `db.go`, it can contain configuration of different databases/services like `redis`, `kafka`, etc.

### `/internal/handlers`

This folder consists of the HTTP handlers which defines how the requests will be processed.

```go
// userHandler.go
package handlers

import (
	"net/http"

	"github.com/Aniketyadav44/go-backend-template/internal/services"
	"github.com/gin-gonic/gin"
)

type UserHandler struct {
	service *services.UserService
}

func NewUserHandler(service *services.UserService) *UserHandler {
	return &UserHandler{
		service: service,
	}
}

// ... handler functions defined here
func (h *UserHandler) GetAllUsers(c *gin.Context) {
	users, err := h.service.GetAllUsers()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"users": users})
}
```

Here, we have defined a `UserHandler` struct which has a reference to it’s `service`. This `service` contains db connection injected which holds all of the business logic for specific handler functions to be called on specific routes.

Then, a constructor function `NewUserHandler()` is defined which basically initializes and returns a new handler with service dependency injected.

### `/internal/services`

This folder consists of the services which holds all of the business logic for specific handlers.

```go
// userService.go
package services

import (
	"database/sql"

	"github.com/Aniketyadav44/go-backend-template/internal/models"
)

type UserService struct {
	db *sql.DB
}

func NewUserService(db *sql.DB) *UserService {
	return &UserService{
		db: db,
	}
}

func (s *UserService) GetAllUsers() ([]models.User, error) {
	// ... get users logic from database connection -> s.db
}
```

Here, we have defined `UserService` struct that holds a reference to the database connection (`*sql.DB`).

Then, there is a constructor function `NewUserService()` which initializes and returns new instance of service with the database connection injected.

### `/internal/models`

This folder consists of the different data structures used across our application. This also contains the structures for our request body of POST APIs

```go
// userModel.go example
package models

type User struct {
	ID        string    `json:"id"`
	Username  string    `json:"username"`
	Email     string    `json:"email"`
}
```

### `/internal/middlewares`

This folder consists of the different middlewares used for our APIs divided into different files.

```go
// authMiddleware.go
package middlewares

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// ...logic
		c.Next()
	}
}
```

### `/pkg`

This folder consists of various sub-folders such as `responses`, `utils`, etc which holds all of the re-usable code to be imported across our application.

for e.g.

* `/pkg/utils/utils.go` can consist of common utilities functions
    
* `/pkg/utils/crypto.go` can consist of the encryption, decryption, hashing, etc functions
    
* `/pkg/responses/errorResponses.go` can consist of different error responses to be sent in error conditions.
    

## Conclusion

Designing a clean and modular structure is very important for a scalable and maintainable codebase. By organizing code files into different logical layers, we make our project easier to understand and extend.

This `gin` based template can be used to quickly get started with a new project.

---

This template can be found here:

%[https://github.com/Aniketyadav44/go-backend-template] 

If you find this article helpful, don't forget to hit the ❤️ button.

Check out my website [**here**](https://anikety.com) and feel free to connect.

Happy Coding! 👨‍💻
