---
title: "GN004 – Use Dependency Injection, Not Global Variables"
impact: high
impactDescription: "Global database connections and service instances in package-level vars are untestable, race-prone, and make initialization order fragile."
tags: [go, gin, architecture, testing]
---

# GN004 – Use Dependency Injection, Not Global Variables

## Rule

Database connections, service instances, configuration objects, and HTTP clients must be injected as struct fields into handlers. Never declare them as `var db *gorm.DB` at package level and access them from handler functions.

## Why

Global state:
- Cannot be replaced with a mock in unit tests.
- Initialization order is implicit — the global may be `nil` if setup hasn't run.
- Race conditions when tests share globals.

Dependency injection via struct fields makes dependencies explicit, swappable, and testable.

## Wrong

```go
// db/db.go — package-level global
var DB *gorm.DB

func Init() {
    DB, _ = gorm.Open(...)
}

// handlers/user.go — accesses global directly
func GetUser(c *gin.Context) {
    var user User
    db.DB.First(&user, c.Param("id"))   // ❌ depends on global initialization order
    c.JSON(200, user)
}
```

## Correct

```go
// handlers/user_handler.go
type UserHandler struct {
    db      *gorm.DB         // injected
    service UserService      // interface — swappable in tests
    logger  *slog.Logger
}

func NewUserHandler(db *gorm.DB, svc UserService, logger *slog.Logger) *UserHandler {
    return &UserHandler{db: db, service: svc, logger: logger}
}

func (h *UserHandler) GetUser(c *gin.Context) {
    ctx := c.Request.Context()
    user, err := h.service.FindUserByID(ctx, c.Param("id"))
    if err != nil {
        c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "not found"})
        return
    }
    c.JSON(http.StatusOK, user)
}

// main.go — wiring
db := database.Connect(cfg.DSN)
userSvc := services.NewUserService(db)
userHandler := handlers.NewUserHandler(db, userSvc, logger)

r := gin.New()
r.GET("/users/:id", userHandler.GetUser)
```

## Notes

- Define service dependencies as Go interfaces so handlers can be unit-tested with mocks without a real DB.
- Use a DI framework (e.g., `google/wire`, `samber/do`) for large projects to automate wiring.
- Configuration (`Config` structs) should be loaded once in `main` and injected — never read from `os.Getenv` directly inside handlers.
