Understanding Dependency Injection in Go Simply
Dependency Injection (DI) is often considered a complex concept because it is associated with large frameworks. However, in the Go programming language, Dependency Injection is actually very simple and does not require additional frameworks (like Wire or Dig) for most applications.
What is Dependency Injection?
Simply put, Dependency Injection means we pass the dependencies (like database connections or third-party clients) required by a function/struct, instead of letting it instantiate or search for them itself from global variables.
Without DI Approach (Bad)
package handler
import "database/sql"
var DB *sql.DB // Global variable
func GetUserHandler(w http.ResponseWriter, r *http.Request) {
// Tightly coupled to a global database variable
rows, err := DB.Query("SELECT id, username FROM users")
// ...
}
This approach makes unit testing difficult because the handler is tightly coupled to a global database variable.
With DI Approach (Good)
The best way in Go is to create a handler struct that accepts a database interface during initialization:
type UserHandler struct {
db *sql.DB
}
func NewUserHandler(db *sql.DB) *UserHandler {
return &UserHandler{db: db}
}
func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) {
// Use h.db here
}
Conclusion
By implementing simple Dependency Injection through struct constructors (like NewUserHandler), your Go application code becomes much easier to test, flexible, and exceptionally modular.