Dependency Injection (DI) in Go (Golang) follows the same basic principles as in other programming languages, but with a focus on simplicity and explicitness, given Go’s minimalist design. DI is used to decouple the creation of an object’s dependencies from its behavior, making the code more modular, testable, and maintainable.

Step-by-Step Explanation of Dependency Injection in Go

  1. Define Interfaces for Dependencies:

    • Identify the components of your application that can be abstracted behind interfaces. These interfaces represent the contracts that your components must fulfill.
    • Example: If a component needs to access a database, define an interface for database operations.
  2. Implement the Dependencies:

    • Create concrete implementations of these interfaces. These implementations are the actual dependencies that will be injected.
    • Example: Implement the database interface with a MySQL or PostgreSQL driver.
  3. Design Your Components to Receive Dependencies:

    • Write your components (such as structs and their methods in Go) to receive dependencies through their constructors or setter methods. This is where the actual injection happens.
    • Example: A service struct takes a database interface implementation as a parameter in its constructor.
  4. Create a Centralized Place for Dependency Construction (Optional):

    • While not a requirement, some applications benefit from having a central place where dependencies are constructed. This can be a main function or a special factory function.
    • This step often involves “wiring up” your application by creating the concrete dependencies and passing them to the components that need them.
  5. Inject the Dependencies:

    • Instantiate your dependencies and inject them into the components that require them.
    • Example: In your main function, create a database connection and pass it to the service constructor.
  6. Use the Components:

    • With dependencies injected, your components are ready to be used. The key point here is that the components are not aware of how their dependencies are created, making them easier to test and maintain.

Example in Go

Here’s a simple example to illustrate DI in Go:

package main
 
import "fmt"
 
// Database is an interface for database operations
type Database interface {
    Query(query string) string
}
 
// MySQL implements Database interface
type MySQL struct {}
 
func (db MySQL) Query(query string) string {
    // Implementation for MySQL query
    return "MySQL result for " + query
}
 
// Service uses a Database dependency
type Service struct {
    db Database
}
 
func NewService(db Database) *Service {
    return &Service{db: db}
}
 
func (s *Service) PerformAction(query string) string {
    // Use the database dependency
    return s.db.Query(query)
}
 
func main() {
    // Dependency is created and injected here
    mysql := MySQL{}
    service := NewService(mysql)
 
    // Use the service
    result := service.PerformAction("SELECT * FROM users")
    fmt.Println(result)
}

In this example:

  • Database is an interface that abstracts database operations.
  • MySQL is a concrete implementation of the Database interface.
  • Service is a struct that depends on the Database interface. It receives its dependency through its constructor NewService.
  • In the main function, we create a MySQL instance and inject it into a new Service instance.

This structure makes it easy to swap out the MySQL implementation with another implementation of the Database interface, such as a mock database for testing, without changing the Service code.

Mocking

https://quii.gitbook.io/learn-go-with-tests/go-fundamentals/dependency-injection