https://github.com/samber/do
⚙️ A dependency injection toolkit based on Go 1.18+ Generics.
https://github.com/samber/do
container dependency dependency-graph di generics go golang graceful-shutdown healthcheck injection injector invoke ioc lifecycle provide service typesafe
Last synced: 6 months ago
JSON representation
⚙️ A dependency injection toolkit based on Go 1.18+ Generics.
- Host: GitHub
- URL: https://github.com/samber/do
- Owner: samber
- License: mit
- Created: 2022-05-17T22:10:38.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2025-05-05T14:16:54.000Z (6 months ago)
- Last Synced: 2025-05-05T15:36:27.463Z (6 months ago)
- Topics: container, dependency, dependency-graph, di, generics, go, golang, graceful-shutdown, healthcheck, injection, injector, invoke, ioc, lifecycle, provide, service, typesafe
- Language: Go
- Homepage: https://pkg.go.dev/github.com/samber/do
- Size: 1.82 MB
- Stars: 2,054
- Watchers: 24
- Forks: 82
- Open Issues: 45
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-go - do - A dependency injection framework based on Generics. (Miscellaneous / Dependency Injection)
- zero-alloc-awesome-go - do - A dependency injection framework based on Generics. (Miscellaneous / Dependency Injection)
- awesome-go-extra - do - 05-17T22:10:38Z|2022-08-06T23:51:11Z| (Microsoft Office / Dependency Injection)
- my-awesome - samber/do - graph,di,generics,go,golang,graceful-shutdown,healthcheck,injection,injector,invoke,ioc,lifecycle,provide,service,typesafe pushed_at:2025-10 star:2.5k fork:0.1k ⚙️ A dependency injection toolkit based on Go 1.18+ Generics. (Go)
- awesome-ccamel - samber/do - ⚙️ A dependency injection toolkit based on Go 1.18+ Generics. (Go)
- awesome-go - do - A dependency injection framework based on Generics. Stars:`2.3K`. (Miscellaneous / Dependency Injection)
- awesome-repositories - samber/do - ⚙️ A dependency injection toolkit based on Go 1.18+ Generics. (Go)
README
# do - Dependency Injection
[](https://github.com/samber/do/releases)

[](https://pkg.go.dev/github.com/samber/do)

[](https://goreportcard.com/report/github.com/samber/do)
[](https://codecov.io/gh/samber/do)
[](./LICENSE)
**⚙️ A dependency injection toolkit based on Go 1.18+ Generics.**
This library implements the Dependency Injection design pattern. It may replace the `uber/dig` fantastic package in simple Go projects. `samber/do` uses Go 1.18+ generics and therefore offers a typesafe API.
**See also:**
- [samber/lo](https://github.com/samber/lo): A Lodash-style Go library based on Go 1.18+ Generics
- [samber/mo](https://github.com/samber/mo): Monads based on Go 1.18+ Generics (Option, Result, Either...)
**Why this name?**
I love **short name** for such a utility library. This name is the sum of `DI` and `Go` and no Go package currently uses this name.
**⭕⭕⭕⭕⭕⭕ About v2 ⭕⭕⭕⭕⭕⭕**
Check out the beta now!
```bash
go get -u github.com/samber/do/v2@v2.0.0-beta.7
```
Documentation: https://do.samber.dev/
Please report bugs here: [#45](https://github.com/samber/do/pull/45).
## 💡 Features
- Service registration
- Service invocation
- Service health check
- Service shutdown
- Service lifecycle hooks
- Named or anonymous services
- Eagerly or lazily loaded services
- Dependency graph resolution
- Default injector
- Injector cloning
- Service override
- Lightweight, no dependencies
- No code generation
🚀 Services are loaded in invocation order.
🕵️ Service health can be checked individually or globally. Services implementing `do.Healthcheckable` interface will be called via `do.HealthCheck[type]()` or `injector.HealthCheck()`.
🛑 Services can be shutdowned properly, in back-initialization order. Services implementing `do.Shutdownable` interface will be called via `do.Shutdown[type]()` or `injector.Shutdown()`.
## 🚀 Install
```sh
go get github.com/samber/do@v1
```
This library is v1 and follows SemVer strictly.
No breaking changes will be made to exported APIs before v2.0.0.
This library has no dependencies except the Go std lib.
## 💡 Quick start
You can import `do` using:
```go
import (
"github.com/samber/do"
)
```
Then instantiate services:
```go
func main() {
injector := do.New()
// provides CarService
do.Provide(injector, NewCarService)
// provides EngineService
do.Provide(injector, NewEngineService)
car := do.MustInvoke[*CarService](injector)
car.Start()
// prints "car starting"
do.HealthCheck[EngineService](injector)
// returns "engine broken"
// injector.ShutdownOnSIGTERM() // will block until receiving sigterm signal
injector.Shutdown()
// prints "car stopped"
}
```
Services:
```go
type EngineService interface{}
func NewEngineService(i *do.Injector) (EngineService, error) {
return &engineServiceImplem{}, nil
}
type engineServiceImplem struct {}
// [Optional] Implements do.Healthcheckable.
func (c *engineServiceImplem) HealthCheck() error {
return fmt.Errorf("engine broken")
}
```
```go
func NewCarService(i *do.Injector) (*CarService, error) {
engine := do.MustInvoke[EngineService](i)
car := CarService{Engine: engine}
return &car, nil
}
type CarService struct {
Engine EngineService
}
func (c *CarService) Start() {
println("car starting")
}
// [Optional] Implements do.Shutdownable.
func (c *CarService) Shutdown() error {
println("car stopped")
return nil
}
```
## 🤠 Spec
[GoDoc: https://godoc.org/github.com/samber/do](https://godoc.org/github.com/samber/do)
Injector:
- [do.New](https://pkg.go.dev/github.com/samber/do#New)
- [do.NewWithOpts](https://pkg.go.dev/github.com/samber/do#NewWithOpts)
- [injector.Clone](https://pkg.go.dev/github.com/samber/do#injector.Clone)
- [injector.CloneWithOpts](https://pkg.go.dev/github.com/samber/do#injector.CloneWithOpts)
- [injector.HealthCheck](https://pkg.go.dev/github.com/samber/do#injector.HealthCheck)
- [injector.Shutdown](https://pkg.go.dev/github.com/samber/do#injector.Shutdown)
- [injector.ShutdownOnSIGTERM](https://pkg.go.dev/github.com/samber/do#injector.ShutdownOnSIGTERM)
- [injector.ShutdownOnSignals](https://pkg.go.dev/github.com/samber/do#injector.ShutdownOnSignals)
- [injector.ListProvidedServices](https://pkg.go.dev/github.com/samber/do#injector.ListProvidedServices)
- [injector.ListInvokedServices](https://pkg.go.dev/github.com/samber/do#injector.ListInvokedServices)
- [do.HealthCheck](https://pkg.go.dev/github.com/samber/do#HealthCheck)
- [do.HealthCheckNamed](https://pkg.go.dev/github.com/samber/do#HealthCheckNamed)
- [do.Shutdown](https://pkg.go.dev/github.com/samber/do#Shutdown)
- [do.ShutdownNamed](https://pkg.go.dev/github.com/samber/do#ShutdownNamed)
- [do.MustShutdown](https://pkg.go.dev/github.com/samber/do#MustShutdown)
- [do.MustShutdownNamed](https://pkg.go.dev/github.com/samber/do#MustShutdownNamed)
Service registration:
- [do.Provide](https://pkg.go.dev/github.com/samber/do#Provide)
- [do.ProvideNamed](https://pkg.go.dev/github.com/samber/do#ProvideNamed)
- [do.ProvideNamedValue](https://pkg.go.dev/github.com/samber/do#ProvideNamedValue)
- [do.ProvideValue](https://pkg.go.dev/github.com/samber/do#ProvideValue)
Service invocation:
- [do.Invoke](https://pkg.go.dev/github.com/samber/do#Invoke)
- [do.MustInvoke](https://pkg.go.dev/github.com/samber/do#MustInvoke)
- [do.InvokeNamed](https://pkg.go.dev/github.com/samber/do#InvokeNamed)
- [do.MustInvokeNamed](https://pkg.go.dev/github.com/samber/do#MustInvokeNamed)
Service override:
- [do.Override](https://pkg.go.dev/github.com/samber/do#Override)
- [do.OverrideNamed](https://pkg.go.dev/github.com/samber/do#OverrideNamed)
- [do.OverrideNamedValue](https://pkg.go.dev/github.com/samber/do#OverrideNamedValue)
- [do.OverrideValue](https://pkg.go.dev/github.com/samber/do#OverrideValue)
### Injector (DI container)
Build a container for your components. `Injector` is responsible for building services in the right order, and managing service lifecycle.
```go
injector := do.New()
```
Or use `nil` as the default injector:
```go
do.Provide(nil, func (i *Injector) (int, error) {
return 42, nil
})
service := do.MustInvoke[int](nil)
```
You can check health of services implementing `func HealthCheck() error`.
```go
type DBService struct {
db *sql.DB
}
func (s *DBService) HealthCheck() error {
return s.db.Ping()
}
injector := do.New()
do.Provide(injector, ...)
do.Invoke(injector, ...)
statuses := injector.HealthCheck()
// map[string]error{
// "*DBService": nil,
// }
```
De-initialize all compoments properly. Services implementing `func Shutdown() error` will be called synchronously in back-initialization order.
```go
type DBService struct {
db *sql.DB
}
func (s *DBService) Shutdown() error {
return s.db.Close()
}
injector := do.New()
do.Provide(injector, ...)
do.Invoke(injector, ...)
// shutdown all services in reverse order
injector.Shutdown()
```
List services:
```go
type DBService struct {
db *sql.DB
}
injector := do.New()
do.Provide(injector, ...)
println(do.ListProvidedServices())
// output: []string{"*DBService"}
do.Invoke(injector, ...)
println(do.ListInvokedServices())
// output: []string{"*DBService"}
```
### Service registration
Services can be registered in multiple way:
- with implicit name (struct or interface name)
- with explicit name
- eagerly
- lazily
Anonymous service, loaded lazily:
```go
type DBService struct {
db *sql.DB
}
do.Provide[DBService](injector, func(i *Injector) (*DBService, error) {
db, err := sql.Open(...)
if err != nil {
return nil, err
}
return &DBService{db: db}, nil
})
```
Named service, loaded lazily:
```go
type DBService struct {
db *sql.DB
}
do.ProvideNamed(injector, "dbconn", func(i *Injector) (*DBService, error) {
db, err := sql.Open(...)
if err != nil {
return nil, err
}
return &DBService{db: db}, nil
})
```
Anonymous service, loaded eagerly:
```go
type Config struct {
uri string
}
do.ProvideValue[Config](injector, Config{uri: "postgres://user:pass@host:5432/db"})
```
Named service, loaded eagerly:
```go
type Config struct {
uri string
}
do.ProvideNamedValue(injector, "configuration", Config{uri: "postgres://user:pass@host:5432/db"})
```
### Service invocation
Loads anonymous service:
```go
type DBService struct {
db *sql.DB
}
dbService, err := do.Invoke[DBService](injector)
```
Loads anonymous service or panics if service was not registered:
```go
type DBService struct {
db *sql.DB
}
dbService := do.MustInvoke[DBService](injector)
```
Loads named service:
```go
config, err := do.InvokeNamed[Config](injector, "configuration")
```
Loads named service or panics if service was not registered:
```go
config := do.MustInvokeNamed[Config](injector, "configuration")
```
### Individual service healthcheck
Check health of anonymous service:
```go
type DBService struct {
db *sql.DB
}
dbService, err := do.Invoke[DBService](injector)
err = do.HealthCheck[DBService](injector)
```
Check health of named service:
```go
config, err := do.InvokeNamed[Config](injector, "configuration")
err = do.HealthCheckNamed(injector, "configuration")
```
### Individual service shutdown
Unloads anonymous service:
```go
type DBService struct {
db *sql.DB
}
dbService, err := do.Invoke[DBService](injector)
err = do.Shutdown[DBService](injector)
```
Unloads anonymous service or panics if service was not registered:
```go
type DBService struct {
db *sql.DB
}
dbService := do.MustInvoke[DBService](injector)
do.MustShutdown[DBService](injector)
```
Unloads named service:
```go
config, err := do.InvokeNamed[Config](injector, "configuration")
err = do.ShutdownNamed(injector, "configuration")
```
Unloads named service or panics if service was not registered:
```go
config := do.MustInvokeNamed[Config](injector, "configuration")
do.MustShutdownNamed(injector, "configuration")
```
### Service override
By default, providing a service twice will panic. Service can be replaced at runtime using `do.Override` helper.
```go
do.Provide[Vehicle](injector, func (i *do.Injector) (Vehicle, error) {
return &CarImplem{}, nil
})
do.Override[Vehicle](injector, func (i *do.Injector) (Vehicle, error) {
return &BusImplem{}, nil
})
```
### Hooks
2 lifecycle hooks are available in Injectors:
- After registration
- After shutdown
```go
injector := do.NewWithOpts(&do.InjectorOpts{
HookAfterRegistration: func(injector *do.Injector, serviceName string) {
fmt.Printf("Service registered: %s\n", serviceName)
},
HookAfterShutdown: func(injector *do.Injector, serviceName string) {
fmt.Printf("Service stopped: %s\n", serviceName)
},
Logf: func(format string, args ...any) {
log.Printf(format, args...)
},
})
```
### Cloning injector
Cloned injector have same service registrations as it's parent, but it doesn't share invoked service state.
Clones are useful for unit testing by replacing some services to mocks.
```go
var injector *do.Injector;
func init() {
do.Provide[Service](injector, func (i *do.Injector) (Service, error) {
return &RealService{}, nil
})
do.Provide[*App](injector, func (i *do.Injector) (*App, error) {
return &App{i.MustInvoke[Service](i)}, nil
})
}
func TestService(t *testing.T) {
i := injector.Clone()
defer i.Shutdown()
// replace Service to MockService
do.Override[Service](i, func (i *do.Injector) (Service, error) {
return &MockService{}, nil
}))
app := do.Invoke[*App](i)
// do unit testing with mocked service
}
```
## 🛩 Benchmark
// @TODO
## 🤝 Contributing
- Ping me on twitter [@samuelberthe](https://twitter.com/samuelberthe) (DMs, mentions, whatever :))
- Fork the [project](https://github.com/samber/do)
- Fix [open issues](https://github.com/samber/do/issues) or request new features
Don't hesitate ;)
### With Docker
```bash
docker-compose run --rm dev
```
### Without Docker
```bash
# Install some dev dependencies
make tools
# Run tests
make test
# or
make watch-test
```
## 👤 Contributors

## 💫 Show your support
Give a ⭐️ if this project helped you!
[](https://github.com/sponsors/samber)
## 📝 License
Copyright © 2022 [Samuel Berthe](https://github.com/samber).
This project is [MIT](./LICENSE) licensed.