https://github.com/quinnjr/go-dependency-injector
A lightweight, type-safe dependency injection (DI) and IoC container for Go. Features: generics, constructor injection, singleton/transient/scoped lifetimes, automatic resolution, thread-safe.
https://github.com/quinnjr/go-dependency-injector
constructor-injection constructor-injector dependency-injection dependency-management di di-container generics go golang inversion-of-control inversion-of-control-container ioc service-container
Last synced: about 1 month ago
JSON representation
A lightweight, type-safe dependency injection (DI) and IoC container for Go. Features: generics, constructor injection, singleton/transient/scoped lifetimes, automatic resolution, thread-safe.
- Host: GitHub
- URL: https://github.com/quinnjr/go-dependency-injector
- Owner: quinnjr
- License: mit
- Created: 2025-12-13T21:05:40.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2026-04-15T18:52:08.000Z (3 months ago)
- Last Synced: 2026-04-15T20:33:30.361Z (3 months ago)
- Topics: constructor-injection, constructor-injector, dependency-injection, dependency-management, di, di-container, generics, go, golang, inversion-of-control, inversion-of-control-container, ioc, service-container
- Language: Go
- Homepage: https://quinnjr.github.io/go-dependency-injector/
- Size: 165 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
# Go Dependency Injector
[](https://pkg.go.dev/github.com/pegasusheavy/go-dependency-injector)
[](https://goreportcard.com/report/github.com/pegasusheavy/go-dependency-injector)
[](https://opensource.org/licenses/MIT)
[](https://github.com/pegasusheavy/go-dependency-injector)
A lightweight, type-safe **dependency injection (DI) container** and **IoC (Inversion of Control)** framework for Go. This service container leverages Go generics for compile-time safety, automatic constructor injection, and a clean, intuitive API.
**Keywords**: dependency injection, DI, IoC, inversion of control, service container, service locator, dependency container, golang DI, go dependency injection, constructor injection
## Features
- **Type-safe generics** — Compile-time type checking with `Register[T]()` and `Resolve[T]()`
- **Multiple lifetimes** — Transient, Singleton, and Scoped dependency management
- **Automatic resolution** — Constructor parameters are automatically resolved from the container
- **Circular dependency detection** — Fails fast with clear error messages
- **Named registrations** — Register multiple implementations of the same interface
- **Thread-safe** — Safe for concurrent access across goroutines
- **Zero external dependencies** — Uses only the Go standard library
## Installation
```bash
go get github.com/pegasusheavy/go-dependency-injector
```
## Quick Start
```go
package main
import (
"fmt"
"github.com/pegasusheavy/go-dependency-injector/di"
)
// Define interfaces
type Logger interface {
Log(message string)
}
type UserService interface {
GetUser(id int) string
}
// Implement them
type ConsoleLogger struct{}
func (l *ConsoleLogger) Log(message string) {
fmt.Println("[LOG]", message)
}
type DefaultUserService struct {
logger Logger
}
func (s *DefaultUserService) GetUser(id int) string {
s.logger.Log(fmt.Sprintf("Fetching user %d", id))
return fmt.Sprintf("User-%d", id)
}
func main() {
// Create container
container := di.New()
// Register dependencies
di.Register[Logger](container, func() Logger {
return &ConsoleLogger{}
}, di.AsSingleton())
di.Register[UserService](container, func(log Logger) UserService {
return &DefaultUserService{logger: log}
})
// Resolve and use
service := di.MustResolve[UserService](container)
user := service.GetUser(42)
fmt.Println("Got:", user)
}
```
## API Reference
### Creating a Container
```go
container := di.New()
```
### Registering Dependencies
#### Basic Registration with Factory
```go
// Factory with no dependencies
di.Register[Logger](container, func() Logger {
return &ConsoleLogger{}
})
// Factory with dependencies (auto-resolved)
di.Register[UserService](container, func(logger Logger, db Database) UserService {
return &DefaultUserService{logger: logger, db: db}
})
// Factory that can return an error
di.Register[Database](container, func(config Config) (Database, error) {
return NewPostgresDB(config.DatabaseURL())
})
```
#### Register an Existing Instance
```go
config := &AppConfig{Port: 8080}
di.RegisterInstance[Config](container, config)
```
#### Register Interface → Implementation Mapping
```go
di.RegisterType[UserRepository, PostgresUserRepository](container, di.AsSingleton())
```
### Lifetime Options
| Lifetime | Behavior |
|----------|----------|
| `Transient` | New instance created on every resolution (default) |
| `Singleton` | Single instance shared across all resolutions |
| `Scoped` | Single instance per scope (e.g., per HTTP request) |
```go
// Transient (default)
di.Register[Service](c, factory)
di.Register[Service](c, factory, di.AsTransient())
// Singleton
di.Register[Service](c, factory, di.AsSingleton())
// Scoped
di.Register[Service](c, factory, di.AsScoped())
// Using WithLifetime
di.Register[Service](c, factory, di.WithLifetime(di.Singleton))
```
### Resolving Dependencies
```go
// Returns (T, error)
service, err := di.Resolve[UserService](container)
if err != nil {
log.Fatal(err)
}
// Panics on error (use when you know registration exists)
service := di.MustResolve[UserService](container)
```
### Named Registrations
Register multiple implementations of the same interface:
```go
// Register with names
di.Register[Logger](c, func() Logger {
return &ConsoleLogger{}
}, di.WithName("console"))
di.Register[Logger](c, func() Logger {
return &FileLogger{path: "/var/log/app.log"}
}, di.WithName("file"))
// Resolve by name
consoleLogger, _ := di.ResolveNamed[Logger](c, "console")
fileLogger, _ := di.ResolveNamed[Logger](c, "file")
```
### Scoped Resolution
Scopes are useful for request-scoped dependencies in web applications:
```go
di.Register[*RequestContext](c, func() *RequestContext {
return &RequestContext{
RequestID: uuid.New().String(),
StartTime: time.Now(),
}
}, di.AsScoped())
// Per HTTP request
func handler(w http.ResponseWriter, r *http.Request) {
scope := container.CreateScope("request-" + r.Header.Get("X-Request-ID"))
// Same instance within this scope
ctx1, _ := di.ResolveInScope[*RequestContext](container, scope)
ctx2, _ := di.ResolveInScope[*RequestContext](container, scope)
// ctx1 == ctx2 ✓
}
```
### Utility Methods
```go
// Check if a type is registered
if di.Has[Logger](container) {
// ...
}
// Check named registration
if di.HasNamed[Logger](container, "file") {
// ...
}
// Clear all registrations
container.Clear()
```
## Error Handling
The library provides typed errors for precise error handling:
```go
service, err := di.Resolve[UserService](container)
if err != nil {
switch e := err.(type) {
case di.ErrNotRegistered:
log.Printf("Type %s is not registered", e.Type)
case di.ErrCircularDependency:
log.Printf("Circular dependency: %v", e.Chain)
case di.ErrResolutionFailed:
log.Printf("Failed to resolve %s: %v", e.Type, e.Cause)
case di.ErrInvalidFactory:
log.Printf("Invalid factory for %s: %s", e.Type, e.Message)
}
}
```
| Error Type | When It Occurs |
|------------|----------------|
| `ErrNotRegistered` | Attempting to resolve an unregistered type |
| `ErrCircularDependency` | A → B → A dependency chain detected |
| `ErrResolutionFailed` | Factory returned an error or dependency failed |
| `ErrInvalidFactory` | Factory signature is invalid |
| `ErrScopeNotFound` | Referenced scope doesn't exist |
## Complete Example
Here's a realistic example with a layered architecture:
```go
package main
import (
"database/sql"
"log"
"net/http"
"github.com/pegasusheavy/go-dependency-injector/di"
)
// Interfaces
type Config interface {
DatabaseURL() string
Port() string
}
type Logger interface {
Info(msg string)
Error(msg string)
}
type UserRepository interface {
FindByID(id int) (*User, error)
}
type UserService interface {
GetUser(id int) (*User, error)
}
type UserHandler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}
// Bootstrap the application
func main() {
container := di.New()
// Infrastructure layer
di.Register[Config](container, NewEnvConfig, di.AsSingleton())
di.Register[Logger](container, NewZapLogger, di.AsSingleton())
di.Register[*sql.DB](container, func(cfg Config) (*sql.DB, error) {
return sql.Open("postgres", cfg.DatabaseURL())
}, di.AsSingleton())
// Data layer
di.Register[UserRepository](container, NewPostgresUserRepo)
// Business layer
di.Register[UserService](container, NewUserService)
// Presentation layer
di.Register[UserHandler](container, NewUserHandler)
// Start server
handler := di.MustResolve[UserHandler](container)
config := di.MustResolve[Config](container)
log.Printf("Starting server on %s", config.Port())
http.ListenAndServe(config.Port(), handler)
}
```
## Best Practices
### 1. Register at Startup
Register all dependencies during application startup, not at runtime:
```go
func main() {
container := di.New()
registerDependencies(container) // All registrations here
runApplication(container)
}
```
### 2. Depend on Interfaces
Always register and resolve interfaces, not concrete types:
```go
// ✓ Good
di.Register[Logger](c, func() Logger { return &ConsoleLogger{} })
// ✗ Avoid
di.Register[*ConsoleLogger](c, func() *ConsoleLogger { return &ConsoleLogger{} })
```
### 3. Use Appropriate Lifetimes
- **Singleton**: Stateless services, configuration, connection pools
- **Transient**: Stateful objects, request-specific data
- **Scoped**: Per-request context, unit of work patterns
### 4. Keep Factories Simple
Factories should only create objects, not perform business logic:
```go
// ✓ Good
di.Register[UserService](c, func(repo UserRepository, log Logger) UserService {
return &DefaultUserService{repo: repo, logger: log}
})
// ✗ Avoid
di.Register[UserService](c, func(repo UserRepository) UserService {
users, _ := repo.FindAll() // Don't do this!
return &UserService{cachedUsers: users}
})
```
## Testing
The DI container makes testing easy by allowing you to swap implementations:
```go
func TestUserService(t *testing.T) {
container := di.New()
// Register mock dependencies
di.RegisterInstance[Logger](container, &MockLogger{})
di.RegisterInstance[UserRepository](container, &MockUserRepo{
users: map[int]*User{1: {ID: 1, Name: "Test"}},
})
// Register the real service
di.Register[UserService](container, NewUserService)
// Test
service := di.MustResolve[UserService](container)
user, err := service.GetUser(1)
assert.NoError(t, err)
assert.Equal(t, "Test", user.Name)
}
```
## Thread Safety
The container is fully thread-safe. You can safely:
- Register dependencies from multiple goroutines
- Resolve dependencies concurrently
- Create scopes in parallel
## Requirements
- Go 1.22 or later (uses generics)
## License
MIT License - see [LICENSE](LICENSE) for details.
## Package Discovery
This package is automatically indexed by [pkg.go.dev](https://pkg.go.dev/github.com/pegasusheavy/go-dependency-injector) once you create a version tag. Users can:
- Browse documentation at https://pkg.go.dev/github.com/pegasusheavy/go-dependency-injector
- Import directly: `go get github.com/pegasusheavy/go-dependency-injector`
- View examples and API documentation
### For Maintainers
To publish a new version:
```bash
# Create and push a new semantic version tag
git tag v1.0.0
git push origin v1.0.0
# The package will be automatically indexed by pkg.go.dev
# You can verify at: https://pkg.go.dev/github.com/pegasusheavy/go-dependency-injector@v1.0.0
```
## Related Projects
- [uber-go/dig](https://github.com/uber-go/dig) - Reflection-based DI framework
- [google/wire](https://github.com/google/wire) - Compile-time DI code generator
- [samber/do](https://github.com/samber/do) - Generic-based DI container
## Search Terms
If you're looking for any of these, you've found the right package:
- **Dependency Injection in Go** / **Golang DI** - This package provides full DI support
- **IoC Container for Go** / **Inversion of Control** - Implements the IoC pattern
- **Service Container** / **Service Locator** - Acts as a service container for your application
- **Constructor Injection** - Automatically injects dependencies via constructor functions
- **Lifetime Management** - Supports Transient, Singleton, and Scoped lifetimes
- **Interface-based DI** - Designed for programming to interfaces
## FAQ
### Why use this over uber-go/dig or google/wire?
- **Type Safety**: Uses generics for compile-time type checking
- **Simplicity**: Minimal API surface with intuitive methods
- **Zero Dependencies**: No external dependencies
- **Modern Go**: Built for Go 1.22+ with generics
### Can I use this in production?
Yes! The library is fully tested, thread-safe, and follows Go best practices.
### How does performance compare?
See `di/benchmark_test.go` for benchmarks. Performance is comparable to other DI solutions with minimal overhead.
## Contributing
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
Please feel free to submit a Pull Request or open an issue for bugs, features, or questions.