{"id":13413441,"url":"https://github.com/samber/do","last_synced_at":"2025-05-13T20:12:40.639Z","repository":{"id":37426283,"uuid":"493428365","full_name":"samber/do","owner":"samber","description":"⚙️  A dependency injection toolkit based on Go 1.18+ Generics.","archived":false,"fork":false,"pushed_at":"2025-05-05T14:16:54.000Z","size":1907,"stargazers_count":2054,"open_issues_count":45,"forks_count":82,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-05-05T15:36:27.463Z","etag":null,"topics":["container","dependency","dependency-graph","di","generics","go","golang","graceful-shutdown","healthcheck","injection","injector","invoke","ioc","lifecycle","provide","service","typesafe"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/samber/do","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/samber.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["samber"]}},"created_at":"2022-05-17T22:10:38.000Z","updated_at":"2025-05-05T14:16:56.000Z","dependencies_parsed_at":"2023-02-15T09:46:39.138Z","dependency_job_id":"6c5cc979-72ea-4e94-895d-6e76afee581e","html_url":"https://github.com/samber/do","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samber%2Fdo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samber%2Fdo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samber%2Fdo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samber%2Fdo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/samber","download_url":"https://codeload.github.com/samber/do/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254020615,"owners_count":22000755,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["container","dependency","dependency-graph","di","generics","go","golang","graceful-shutdown","healthcheck","injection","injector","invoke","ioc","lifecycle","provide","service","typesafe"],"created_at":"2024-07-30T20:01:40.477Z","updated_at":"2025-05-13T20:12:40.603Z","avatar_url":"https://github.com/samber.png","language":"Go","readme":"\n# do - Dependency Injection\n\n[![tag](https://img.shields.io/github/tag/samber/do.svg)](https://github.com/samber/do/releases)\n![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.18-%23007d9c)\n[![GoDoc](https://godoc.org/github.com/samber/do?status.svg)](https://pkg.go.dev/github.com/samber/do)\n![Build Status](https://github.com/samber/do/actions/workflows/test.yml/badge.svg)\n[![Go report](https://goreportcard.com/badge/github.com/samber/do)](https://goreportcard.com/report/github.com/samber/do)\n[![Coverage](https://img.shields.io/codecov/c/github/samber/do)](https://codecov.io/gh/samber/do)\n[![License](https://img.shields.io/github/license/samber/do)](./LICENSE)\n\n**⚙️ A dependency injection toolkit based on Go 1.18+ Generics.**\n\nThis 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.\n\n**See also:**\n\n- [samber/lo](https://github.com/samber/lo): A Lodash-style Go library based on Go 1.18+ Generics\n- [samber/mo](https://github.com/samber/mo): Monads based on Go 1.18+ Generics (Option, Result, Either...)\n\n**Why this name?**\n\nI 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.\n\n**⭕⭕⭕⭕⭕⭕ About v2 ⭕⭕⭕⭕⭕⭕**\n\nCheck out the beta now!\n\n```bash\ngo get -u github.com/samber/do/v2@v2.0.0-beta.7\n```\n\nDocumentation: https://do.samber.dev/\n\nPlease report bugs here: [#45](https://github.com/samber/do/pull/45).\n\n## 💡 Features\n\n- Service registration\n- Service invocation\n- Service health check\n- Service shutdown\n- Service lifecycle hooks\n- Named or anonymous services\n- Eagerly or lazily loaded services\n- Dependency graph resolution\n- Default injector\n- Injector cloning\n- Service override\n- Lightweight, no dependencies\n- No code generation\n\n🚀 Services are loaded in invocation order.\n\n🕵️ Service health can be checked individually or globally. Services implementing `do.Healthcheckable` interface will be called via `do.HealthCheck[type]()` or `injector.HealthCheck()`.\n\n🛑 Services can be shutdowned properly, in back-initialization order. Services implementing `do.Shutdownable` interface will be called via `do.Shutdown[type]()` or `injector.Shutdown()`.\n\n## 🚀 Install\n\n```sh\ngo get github.com/samber/do@v1\n```\n\nThis library is v1 and follows SemVer strictly.\n\nNo breaking changes will be made to exported APIs before v2.0.0.\n\nThis library has no dependencies except the Go std lib.\n\n## 💡 Quick start\n\nYou can import `do` using:\n\n```go\nimport (\n    \"github.com/samber/do\"\n)\n```\n\nThen instantiate services:\n\n```go\nfunc main() {\n    injector := do.New()\n\n    // provides CarService\n    do.Provide(injector, NewCarService)\n\n    // provides EngineService\n    do.Provide(injector, NewEngineService)\n\n    car := do.MustInvoke[*CarService](injector)\n    car.Start()\n    // prints \"car starting\"\n\n    do.HealthCheck[EngineService](injector)\n    // returns \"engine broken\"\n\n    // injector.ShutdownOnSIGTERM()    // will block until receiving sigterm signal\n    injector.Shutdown()\n    // prints \"car stopped\"\n}\n```\n\nServices:\n\n```go\ntype EngineService interface{}\n\nfunc NewEngineService(i *do.Injector) (EngineService, error) {\n    return \u0026engineServiceImplem{}, nil\n}\n\ntype engineServiceImplem struct {}\n\n// [Optional] Implements do.Healthcheckable.\nfunc (c *engineServiceImplem) HealthCheck() error {\n\treturn fmt.Errorf(\"engine broken\")\n}\n```\n\n```go\nfunc NewCarService(i *do.Injector) (*CarService, error) {\n    engine := do.MustInvoke[EngineService](i)\n    car := CarService{Engine: engine}\n    return \u0026car, nil\n}\n\ntype CarService struct {\n\tEngine EngineService\n}\n\nfunc (c *CarService) Start() {\n\tprintln(\"car starting\")\n}\n\n// [Optional] Implements do.Shutdownable.\nfunc (c *CarService) Shutdown() error {\n\tprintln(\"car stopped\")\n\treturn nil\n}\n```\n\n## 🤠 Spec\n\n[GoDoc: https://godoc.org/github.com/samber/do](https://godoc.org/github.com/samber/do)\n\nInjector:\n\n- [do.New](https://pkg.go.dev/github.com/samber/do#New)\n- [do.NewWithOpts](https://pkg.go.dev/github.com/samber/do#NewWithOpts)\n  - [injector.Clone](https://pkg.go.dev/github.com/samber/do#injector.Clone)\n  - [injector.CloneWithOpts](https://pkg.go.dev/github.com/samber/do#injector.CloneWithOpts)\n  - [injector.HealthCheck](https://pkg.go.dev/github.com/samber/do#injector.HealthCheck)\n  - [injector.Shutdown](https://pkg.go.dev/github.com/samber/do#injector.Shutdown)\n  - [injector.ShutdownOnSIGTERM](https://pkg.go.dev/github.com/samber/do#injector.ShutdownOnSIGTERM)\n  - [injector.ShutdownOnSignals](https://pkg.go.dev/github.com/samber/do#injector.ShutdownOnSignals)\n  - [injector.ListProvidedServices](https://pkg.go.dev/github.com/samber/do#injector.ListProvidedServices)\n  - [injector.ListInvokedServices](https://pkg.go.dev/github.com/samber/do#injector.ListInvokedServices)\n- [do.HealthCheck](https://pkg.go.dev/github.com/samber/do#HealthCheck)\n- [do.HealthCheckNamed](https://pkg.go.dev/github.com/samber/do#HealthCheckNamed)\n- [do.Shutdown](https://pkg.go.dev/github.com/samber/do#Shutdown)\n- [do.ShutdownNamed](https://pkg.go.dev/github.com/samber/do#ShutdownNamed)\n- [do.MustShutdown](https://pkg.go.dev/github.com/samber/do#MustShutdown)\n- [do.MustShutdownNamed](https://pkg.go.dev/github.com/samber/do#MustShutdownNamed)\n\nService registration:\n\n- [do.Provide](https://pkg.go.dev/github.com/samber/do#Provide)\n- [do.ProvideNamed](https://pkg.go.dev/github.com/samber/do#ProvideNamed)\n- [do.ProvideNamedValue](https://pkg.go.dev/github.com/samber/do#ProvideNamedValue)\n- [do.ProvideValue](https://pkg.go.dev/github.com/samber/do#ProvideValue)\n\nService invocation:\n\n- [do.Invoke](https://pkg.go.dev/github.com/samber/do#Invoke)\n- [do.MustInvoke](https://pkg.go.dev/github.com/samber/do#MustInvoke)\n- [do.InvokeNamed](https://pkg.go.dev/github.com/samber/do#InvokeNamed)\n- [do.MustInvokeNamed](https://pkg.go.dev/github.com/samber/do#MustInvokeNamed)\n\nService override:\n\n- [do.Override](https://pkg.go.dev/github.com/samber/do#Override)\n- [do.OverrideNamed](https://pkg.go.dev/github.com/samber/do#OverrideNamed)\n- [do.OverrideNamedValue](https://pkg.go.dev/github.com/samber/do#OverrideNamedValue)\n- [do.OverrideValue](https://pkg.go.dev/github.com/samber/do#OverrideValue)\n\n### Injector (DI container)\n\nBuild a container for your components. `Injector` is responsible for building services in the right order, and managing service lifecycle.\n\n```go\ninjector := do.New()\n```\n\nOr use `nil` as the default injector:\n\n```go\ndo.Provide(nil, func (i *Injector) (int, error) {\n    return 42, nil\n})\n\nservice := do.MustInvoke[int](nil)\n```\n\nYou can check health of services implementing `func HealthCheck() error`.\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\nfunc (s *DBService) HealthCheck() error {\n    return s.db.Ping()\n}\n\ninjector := do.New()\ndo.Provide(injector, ...)\ndo.Invoke(injector, ...)\n\nstatuses := injector.HealthCheck()\n// map[string]error{\n//   \"*DBService\": nil,\n// }\n```\n\nDe-initialize all compoments properly. Services implementing `func Shutdown() error` will be called synchronously in back-initialization order.\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\nfunc (s *DBService) Shutdown() error {\n    return s.db.Close()\n}\n\ninjector := do.New()\ndo.Provide(injector, ...)\ndo.Invoke(injector, ...)\n\n// shutdown all services in reverse order\ninjector.Shutdown()\n```\n\nList services:\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\ninjector := do.New()\n\ndo.Provide(injector, ...)\nprintln(do.ListProvidedServices())\n// output: []string{\"*DBService\"}\n\ndo.Invoke(injector, ...)\nprintln(do.ListInvokedServices())\n// output: []string{\"*DBService\"}\n```\n\n### Service registration\n\nServices can be registered in multiple way:\n\n- with implicit name (struct or interface name)\n- with explicit name\n- eagerly\n- lazily\n\nAnonymous service, loaded lazily:\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\ndo.Provide[DBService](injector, func(i *Injector) (*DBService, error) {\n    db, err := sql.Open(...)\n    if err != nil {\n        return nil, err\n    }\n\n    return \u0026DBService{db: db}, nil\n})\n```\n\nNamed service, loaded lazily:\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\ndo.ProvideNamed(injector, \"dbconn\", func(i *Injector) (*DBService, error) {\n    db, err := sql.Open(...)\n    if err != nil {\n        return nil, err\n    }\n\n    return \u0026DBService{db: db}, nil\n})\n```\n\nAnonymous service, loaded eagerly:\n\n```go\ntype Config struct {\n    uri string\n}\n\ndo.ProvideValue[Config](injector, Config{uri: \"postgres://user:pass@host:5432/db\"})\n```\n\nNamed service, loaded eagerly:\n\n```go\ntype Config struct {\n    uri string\n}\n\ndo.ProvideNamedValue(injector, \"configuration\", Config{uri: \"postgres://user:pass@host:5432/db\"})\n```\n\n### Service invocation\n\nLoads anonymous service:\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\ndbService, err := do.Invoke[DBService](injector)\n```\n\nLoads anonymous service or panics if service was not registered:\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\ndbService := do.MustInvoke[DBService](injector)\n```\n\nLoads named service:\n\n```go\nconfig, err := do.InvokeNamed[Config](injector, \"configuration\")\n```\n\nLoads named service or panics if service was not registered:\n\n```go\nconfig := do.MustInvokeNamed[Config](injector, \"configuration\")\n```\n\n### Individual service healthcheck\n\nCheck health of anonymous service:\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\ndbService, err := do.Invoke[DBService](injector)\nerr = do.HealthCheck[DBService](injector)\n```\n\nCheck health of named service:\n\n```go\nconfig, err := do.InvokeNamed[Config](injector, \"configuration\")\nerr = do.HealthCheckNamed(injector, \"configuration\")\n```\n\n### Individual service shutdown\n\nUnloads anonymous service:\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\ndbService, err := do.Invoke[DBService](injector)\nerr = do.Shutdown[DBService](injector)\n```\n\nUnloads anonymous service or panics if service was not registered:\n\n```go\ntype DBService struct {\n    db *sql.DB\n}\n\ndbService := do.MustInvoke[DBService](injector)\ndo.MustShutdown[DBService](injector)\n```\n\nUnloads named service:\n\n```go\nconfig, err := do.InvokeNamed[Config](injector, \"configuration\")\nerr = do.ShutdownNamed(injector, \"configuration\")\n```\n\nUnloads named service or panics if service was not registered:\n\n```go\nconfig := do.MustInvokeNamed[Config](injector, \"configuration\")\ndo.MustShutdownNamed(injector, \"configuration\")\n```\n\n### Service override\n\nBy default, providing a service twice will panic. Service can be replaced at runtime using `do.Override` helper.\n\n```go\ndo.Provide[Vehicle](injector, func (i *do.Injector) (Vehicle, error) {\n    return \u0026CarImplem{}, nil\n})\n\ndo.Override[Vehicle](injector, func (i *do.Injector) (Vehicle, error) {\n    return \u0026BusImplem{}, nil\n})\n```\n\n### Hooks\n\n2 lifecycle hooks are available in Injectors:\n- After registration\n- After shutdown\n\n```go\ninjector := do.NewWithOpts(\u0026do.InjectorOpts{\n    HookAfterRegistration: func(injector *do.Injector, serviceName string) {\n        fmt.Printf(\"Service registered: %s\\n\", serviceName)\n    },\n    HookAfterShutdown: func(injector *do.Injector, serviceName string) {\n        fmt.Printf(\"Service stopped: %s\\n\", serviceName)\n    },\n\n    Logf: func(format string, args ...any) {\n        log.Printf(format, args...)\n    },\n})\n```\n\n### Cloning injector\n\nCloned injector have same service registrations as it's parent, but it doesn't share invoked service state.\n\nClones are useful for unit testing by replacing some services to mocks.\n\n```go\nvar injector *do.Injector;\n\nfunc init() {\n    do.Provide[Service](injector, func (i *do.Injector) (Service, error) {\n        return \u0026RealService{}, nil\n    })\n    do.Provide[*App](injector, func (i *do.Injector) (*App, error) {\n        return \u0026App{i.MustInvoke[Service](i)}, nil\n    })\n}\n\nfunc TestService(t *testing.T) {\n    i := injector.Clone()\n    defer i.Shutdown()\n\n    // replace Service to MockService\n    do.Override[Service](i, func (i *do.Injector) (Service, error) {\n        return \u0026MockService{}, nil\n    }))\n\n    app := do.Invoke[*App](i)\n    // do unit testing with mocked service\n}\n```\n\n## 🛩 Benchmark\n\n// @TODO\n\n## 🤝 Contributing\n\n- Ping me on twitter [@samuelberthe](https://twitter.com/samuelberthe) (DMs, mentions, whatever :))\n- Fork the [project](https://github.com/samber/do)\n- Fix [open issues](https://github.com/samber/do/issues) or request new features\n\nDon't hesitate ;)\n\n### With Docker\n\n```bash\ndocker-compose run --rm dev\n```\n\n### Without Docker\n\n```bash\n# Install some dev dependencies\nmake tools\n\n# Run tests\nmake test\n# or\nmake watch-test\n```\n\n## 👤 Contributors\n\n![Contributors](https://contrib.rocks/image?repo=samber/do)\n\n## 💫 Show your support\n\nGive a ⭐️ if this project helped you!\n\n[![GitHub Sponsors](https://img.shields.io/github/sponsors/samber?style=for-the-badge)](https://github.com/sponsors/samber)\n\n## 📝 License\n\nCopyright © 2022 [Samuel Berthe](https://github.com/samber).\n\nThis project is [MIT](./LICENSE) licensed.\n","funding_links":["https://github.com/sponsors/samber"],"categories":["Miscellaneous","Go","杂项","Microsoft Office"],"sub_categories":["Dependency Injection","依赖注入"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamber%2Fdo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsamber%2Fdo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamber%2Fdo/lists"}