Skip to content

Instantly share code, notes, and snippets.

@emad-elsaid
Last active July 5, 2025 18:52
Show Gist options
  • Save emad-elsaid/7cef474037dd3f5afbdc0cce87300180 to your computer and use it in GitHub Desktop.
Save emad-elsaid/7cef474037dd3f5afbdc0cce87300180 to your computer and use it in GitHub Desktop.
Dependency injection
package main
import (
"database/sql"
"fmt"
"log/slog"
"reflect"
"sync"
)
func main() {
app := new(App)
RegisterResource(app, "db", NewDB)
// Get it later
db, err := GetResource[*sql.DB](app, "db")
if err != nil {
slog.Error("Failed to get resource", "error", err)
return
}
slog.Info("Hello, World!", "db", db)
}
// App contains all application wide instances and shared resources
type App struct {
// resources is a map from the type of resource -> a map from resource instance ID to its factory
// So every resource type can have multiple instances registered in runtime and retrieved when needed
// for example a DBPool factory. Can be registered twice once for master DB and another for readonly DB
resources sync.Map
}
// Register the factory of this type with the given name. it will be executed
// only once and the return instance/error will be reused (uses sync.Once)
func RegisterResource[T any](app *App, id string, factory func() (T, error)) {
var t T
fs, _ := app.resources.LoadOrStore(reflect.TypeOf(t), new(sync.Map))
factories := fs.(*sync.Map)
once := sync.OnceValues(factory)
factories.Store(id, once)
}
func GetResource[T any](app *App, id string) (T, error) {
var t T
fs, ok := app.resources.Load(reflect.TypeOf(t))
if !ok {
return *new(T), fmt.Errorf("Resource not found: %s", id)
}
factories := fs.(*sync.Map)
f, ok := factories.Load(id)
if !ok {
return *new(T), fmt.Errorf("Factory not found: %s", id)
}
factory, ok := f.(func() (T, error))
if !ok {
return *new(T), fmt.Errorf("Failed to get cast resource factory: %s", id)
}
return factory()
}
func NewDB() (*sql.DB, error) {
// init new db here
return &sql.DB{}, nil
}
package main
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
)
// Test types for dependency injection
type TestService struct {
Name string
}
type AnotherService struct {
ID int
}
type Config struct {
Value string
}
func TestRegisterResource(t *testing.T) {
t.Run("should register resource factory successfully", func(t *testing.T) {
app := &App{}
factory := func() (TestService, error) {
return TestService{Name: "test"}, nil
}
RegisterResource(app, "test-service", factory)
// Verify the resource was registered by trying to get it
result, err := GetResource[TestService](app, "test-service")
require.NoError(t, err)
require.Equal(t, "test", result.Name)
})
t.Run("should register multiple resources of same type", func(t *testing.T) {
app := &App{}
factory1 := func() (TestService, error) {
return TestService{Name: "service1"}, nil
}
factory2 := func() (TestService, error) {
return TestService{Name: "service2"}, nil
}
RegisterResource(app, "service1", factory1)
RegisterResource(app, "service2", factory2)
// Verify both resources were registered
result1, err := GetResource[TestService](app, "service1")
require.NoError(t, err)
require.Equal(t, "service1", result1.Name)
result2, err := GetResource[TestService](app, "service2")
require.NoError(t, err)
require.Equal(t, "service2", result2.Name)
})
t.Run("should register different resource types", func(t *testing.T) {
app := &App{}
testFactory := func() (TestService, error) {
return TestService{Name: "test"}, nil
}
anotherFactory := func() (AnotherService, error) {
return AnotherService{ID: 123}, nil
}
configFactory := func() (Config, error) {
return Config{Value: "config"}, nil
}
RegisterResource(app, "test-service", testFactory)
RegisterResource(app, "another-service", anotherFactory)
RegisterResource(app, "config", configFactory)
// Verify all resources were registered
testResult, err := GetResource[TestService](app, "test-service")
require.NoError(t, err)
require.Equal(t, "test", testResult.Name)
anotherResult, err := GetResource[AnotherService](app, "another-service")
require.NoError(t, err)
require.Equal(t, 123, anotherResult.ID)
configResult, err := GetResource[Config](app, "config")
require.NoError(t, err)
require.Equal(t, "config", configResult.Value)
})
}
func TestGetResource(t *testing.T) {
t.Run("should get registered resource successfully", func(t *testing.T) {
app := &App{}
expectedService := TestService{Name: "test-service"}
factory := func() (TestService, error) {
return expectedService, nil
}
RegisterResource(app, "test-service", factory)
result, err := GetResource[TestService](app, "test-service")
require.NoError(t, err)
require.Equal(t, expectedService, result)
})
t.Run("should return error when resource type not found", func(t *testing.T) {
app := &App{}
// Try to get a resource type that was never registered
result, err := GetResource[TestService](app, "non-existent")
require.Error(t, err)
require.Contains(t, err.Error(), "Resource not found: non-existent")
require.Equal(t, TestService{}, result)
})
t.Run("should return error when factory not found for type", func(t *testing.T) {
app := &App{}
// Register a different type first
anotherFactory := func() (AnotherService, error) {
return AnotherService{ID: 123}, nil
}
RegisterResource(app, "another-service", anotherFactory)
// Try to get TestService with name that doesn't exist for this type
result, err := GetResource[TestService](app, "non-existent")
require.Error(t, err)
require.Contains(t, err.Error(), "Resource not found: non-existent")
require.Equal(t, TestService{}, result)
})
t.Run("should return error when factory name not found", func(t *testing.T) {
app := &App{}
factory := func() (TestService, error) {
return TestService{Name: "test"}, nil
}
RegisterResource(app, "test-service", factory)
// Try to get with wrong name
result, err := GetResource[TestService](app, "wrong-name")
require.Error(t, err)
require.Contains(t, err.Error(), "Factory not found: wrong-name")
require.Equal(t, TestService{}, result)
})
t.Run("should handle factory that returns error", func(t *testing.T) {
app := &App{}
expectedError := errors.New("factory error")
factory := func() (TestService, error) {
return TestService{}, expectedError
}
RegisterResource(app, "error-service", factory)
result, err := GetResource[TestService](app, "error-service")
require.Error(t, err)
require.Equal(t, expectedError, err)
require.Equal(t, TestService{}, result)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment