do

package module
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 21, 2025 License: MIT Imports: 19 Imported by: 163

README

do - Dependency Injection

tag Go Version GoDoc Build Status Go report Coverage License

⚙️ A dependency injection toolkit based on Go 1.18+ Generics.

This library implements the Dependency Injection design pattern. It may replace the fantastic uber/dig package. samber/do uses Go 1.18+ generics and therefore offers a type‑safe API.

image

See also:

  • samber/lo: A Lodash-style Go library based on Go 1.18+ Generics
  • samber/mo: Monads based on Go 1.18+ Generics (Option, Result, Either...)

Why this name?

I love the short name for such a utility library. This name is the sum of DI and Go and no Go package uses this name.

💡 Features

  • 📒 Service registration
    • Register by type
    • Register by name
    • Register multiple services from a package at once
  • 🪃 Service invocation
    • Eager loading
    • Lazy loading
    • Transient loading
    • Tag-based invocation
    • Circular dependency detection
  • 🧙‍♂️ Service aliasing
    • Implicit (provide struct, invoke interface)
    • Explicit (provide struct, bind interface, invoke interface)
  • 🔁 Service lifecycle
    • Health check
    • Graceful unload (shutdown)
    • Dependency-aware parallel shutdown
    • Lifecycle hooks
  • 📦 Scope (a.k.a module) tree
    • Visibility control
    • Dependency grouping
  • 📤 Container
    • Dependency graph resolution and visualization
    • Default container
    • Container cloning
    • Service override
  • 🧪 Debugging & introspection
    • Explain APIs: scope tree and service dependencies
    • Web UI & HTTP middleware (std, Gin, Fiber, Echo, Chi)
  • 🌈 Lightweight, no dependencies
  • 🔅 No code generation
  • 😷 Type‑safe API

🚀 Install

# v2 (latest)
go get github.com/samber/do@v2

# v1
go get github.com/samber/do

This library is v2 and follows SemVer strictly.

No breaking changes will be made to exported APIs before v3.0.0.

This library has no dependencies except the Go std lib.

🔥 Migration from v1 to v2

Documentation here

🤠 Documentation

🤝 Contributing

Don't hesitate ;)

# Install some dev dependencies
make tools

# Run tests
make test
# or
make watch-test

👤 Contributors

Contributors

💫 Show your support

Give a ⭐️ if this project helped you!

GitHub Sponsors

📝 License

Copyright © 2022 Samuel Berthe.

This project is MIT licensed.

Documentation

Index

Constants

View Source
const DefaultRootScopeName = "[root]"

DefaultRootScopeName is the default name for the root scope.

View Source
const DefaultStructTagKey = "do"

DefaultStructTagKey is the default tag key used for struct field injection. When using struct injection, fields can be tagged with `do:""` or `do:"service-name"` to specify which service should be injected.

Variables

View Source
var (
	//nolint:revive
	ErrServiceNotFound    = errors.New("DI: could not find service")
	ErrServiceNotMatch    = errors.New("DI: could not find service satisfying interface")
	ErrCircularDependency = errors.New("DI: circular dependency detected")
	ErrHealthCheckTimeout = errors.New("DI: health check timeout")
)
View Source
var DefaultRootScope = New()

DefaultRootScope is a global instance of the root scope that can be used for simple dependency injection scenarios without creating a custom scope.

View Source
var MaxInvocationFrames uint32 = 100

MaxInvocationFrames defines the maximum number of stack frames to capture when tracking service invocations for debugging and observability purposes.

Functions

func As

func As[Initial any, Alias any](i Injector) error

As declares an alias for a service, allowing it to be retrieved using a different type. This function creates a type alias where the Alias type can be used to retrieve the Initial service. The alias is automatically named using the type name of Alias.

Parameters:

  • i: The injector to register the alias in

Returns an error if the alias cannot be created (e.g., type incompatibility or missing service).

Example:

// Register a concrete service
do.Provide(injector, func(i do.Injector) (*PostgresqlDatabase, error) {
    return &PostgresqlDatabase{}, nil
})

// Create an alias so it can be retrieved as an interface
do.As[*PostgresqlDatabase, Database](injector)

// Now both work:
db := do.MustInvoke[*PostgresqlDatabase](injector)
db := do.MustInvoke[Database](injector)

func AsNamed

func AsNamed[Initial any, Alias any](i Injector, initial string, alias string) error

AsNamed declares a named alias for a named service. This function allows you to create aliases with custom names for both the initial service and the alias.

Parameters:

  • i: The injector to register the alias in
  • initial: The name of the existing service to alias
  • alias: The name for the new alias service

Returns an error if the alias cannot be created (e.g., type incompatibility or missing service).

Example:

// Register a service with a custom name
do.ProvideNamed(injector, "my-db", func(i do.Injector) (*PostgresqlDatabase, error) {
    return &PostgresqlDatabase{}, nil
})

// Create an alias with custom names
do.AsNamed[*PostgresqlDatabase, Database](injector, "my-db", "db-interface")

// Retrieve using the alias name
db := do.MustInvokeNamed[Database](injector, "db-interface")

func Bind

func Bind[Initial any, Alias any]() func(Injector)

Bind creates a function that creates a type alias between two types. This function is a convenience wrapper for creating service binding functions that can be used in packages.

Parameters:

  • Initial: The original type to bind from
  • Alias: The type to bind to (must be implemented by Initial)

Returns a function that creates the type alias when executed. Panics if the binding cannot be created.

Example:

dbBinding := do.Bind[*Database, DatabaseInterface]()

// Global to a package
var Package = do.Package(dbBinding, ...)

func BindNamed

func BindNamed[Initial any, Alias any](initial string, alias string) func(Injector)

BindNamed creates a function that creates a named type alias between two types. This function is a convenience wrapper for creating named service binding functions that can be used in packages.

Parameters:

  • initial: The name of the original service
  • alias: The name for the alias service
  • Initial: The original type to bind from
  • Alias: The type to bind to (must be implemented by Initial)

Returns a function that creates the named type alias when executed. Panics if the binding cannot be created.

Example:

dbBinding := do.BindNamed[*Database, DatabaseInterface]("main-db", "db-interface")

// Global to a package
var Package = do.Package(dbBinding, ...)

func Eager

func Eager[T any](value T) func(Injector)

Eager creates a function that registers an eager service using the default service name. This function is a convenience wrapper for creating eager service registration functions that can be used in packages. The service value is provided directly.

Parameters:

  • value: The service instance to register eagerly

Returns a function that registers the service as eager when executed.

Example:

configService := do.Eager[*Config](&Config{Port: 8080})

// Global to a package
var Package = do.Package(configService)

func EagerNamed

func EagerNamed[T any](serviceName string, value T) func(Injector)

EagerNamed creates a function that registers an eager service with a custom name. This function is a convenience wrapper for creating named eager service registration functions that can be used in packages. The service value is provided directly.

Parameters:

  • serviceName: The custom name for the service
  • value: The service instance to register eagerly

Returns a function that registers the service as eager with the specified name when executed.

Example:

configService := do.EagerNamed[*Config]("app-config", &Config{Port: 8080})

// Global to a package
var Package = do.Package(configService, ...)

func HealthCheck

func HealthCheck[T any](i Injector) error

HealthCheck returns a service status, using type inference to determine the service name. This function performs a health check on a service by inferring its name from the type T. The service must implement either Healthchecker or HealthcheckerWithContext interface.

Parameters:

  • i: The injector containing the service

Returns an error if the health check fails, or nil if the service is healthy.

Example:

err := do.HealthCheck[*Database](injector)
if err != nil {
    log.Printf("Database health check failed: %v", err)
}

func HealthCheckNamed

func HealthCheckNamed(i Injector, name string) error

HealthCheckNamed returns a service status for a named service. This function performs a health check on a service with the specified name. The service must implement either Healthchecker or HealthcheckerWithContext interface.

Parameters:

  • i: The injector containing the service
  • name: The name of the service to health check

Returns an error if the health check fails, or nil if the service is healthy.

Example:

err := do.HealthCheckNamed(injector, "main-database")
if err != nil {
    log.Printf("Main database health check failed: %v", err)
}

func HealthCheckNamedWithContext

func HealthCheckNamedWithContext(ctx context.Context, i Injector, name string) error

HealthCheckNamedWithContext returns a service status for a named service with context support. This function performs a health check on a service with the specified name and context support for timeout and cancellation. The service must implement either Healthchecker or HealthcheckerWithContext interface.

Parameters:

  • ctx: Context for cancellation and timeout control
  • i: The injector containing the service
  • name: The name of the service to health check

Returns an error if the health check fails, or nil if the service is healthy.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

err := do.HealthCheckNamedWithContext(ctx, injector, "main-database")
if err != nil {
    log.Printf("Main database health check failed: %v", err)
}

func HealthCheckWithContext

func HealthCheckWithContext[T any](ctx context.Context, i Injector) error

HealthCheckWithContext returns a service status, using type inference to determine the service name. This function performs a health check on a service with context support for timeout and cancellation. The service must implement either Healthchecker or HealthcheckerWithContext interface.

Parameters:

  • ctx: Context for cancellation and timeout control
  • i: The injector containing the service

Returns an error if the health check fails, or nil if the service is healthy.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

err := do.HealthCheckWithContext[*Database](ctx, injector)
if err != nil {
    log.Printf("Database health check failed: %v", err)
}

func Invoke

func Invoke[T any](i Injector) (T, error)

Invoke retrieves and instantiates a service from the DI container using type inference. The service will be created if it hasn't been instantiated yet (for lazy services).

Example:

service, err := do.Invoke[*MyService](injector)

func InvokeAs

func InvokeAs[T any](i Injector) (T, error)

InvokeAs invokes a service in the DI container by finding the first service that matches the provided type or interface. This function searches through all registered services to find one that can be cast to the requested type T. It's useful when you want to retrieve a service by interface without explicitly creating aliases.

Parameters:

  • i: The injector to search for the service

Returns the service instance and any error that occurred during invocation.

Example:

// Register a concrete service
do.Provide(injector, func(i do.Injector) (*PostgresqlDatabase, error) {
    return &PostgresqlDatabase{}, nil
})

// Retrieve by interface
db, err := do.InvokeAs[Database](injector)

func InvokeNamed

func InvokeNamed[T any](i Injector, name string) (T, error)

InvokeNamed retrieves and instantiates a named service from the DI container. This allows you to retrieve specific named services when multiple services of the same type are registered.

Example:

// Register multiple databases
do.ProvideNamed(injector, "main-db", func(i do.Injector) (*Database, error) {
    return &Database{URL: "postgres://main.acme.dev:5432/db"}, nil
})
do.ProvideNamed(injector, "backup-db", func(i do.Injector) (*Database, error) {
    return &Database{URL: "postgres://backup.acme.dev:5432/db"}, nil
})

// Retrieve specific database
mainDB, err := do.InvokeNamed[*Database](injector, "main-db")
backupDB, err := do.InvokeNamed[*Database](injector, "backup-db")

func InvokeStruct

func InvokeStruct[T any](i Injector) (T, error)

InvokeStruct invokes services located in struct properties. The struct fields must be tagged with `do:""` or `do:"name"`, where `name` is the service name in the DI container. If the service is not found in the DI container, an error is returned. If the service is found but not assignable to the struct field, an error is returned.

Example:

type App struct {
    Database *Database `do:""`
    Logger   *Logger   `do:"app-logger"`
    Config   *Config   `do:""`
}

// Register services
do.Provide(injector, func(i do.Injector) (*Database, error) {
    return &Database{}, nil
})
do.ProvideNamed(injector, "app-logger", func(i do.Injector) (*Logger, error) {
    return &Logger{}, nil
})
do.Provide(injector, func(i do.Injector) (*Config, error) {
    return &Config{}, nil
})

// Invoke struct with injected services
app, err := do.InvokeStruct[App](injector)

func Lazy

func Lazy[T any](p Provider[T]) func(Injector)

Lazy creates a function that registers a lazy service using the default service name. This function is a convenience wrapper for creating lazy service registration functions that can be used in packages.

Parameters:

  • p: The provider function that creates the service instance

Returns a function that registers the service as lazy when executed.

Example:

dbService := do.Lazy[*Database](func(i do.Injector) (*Database, error) {
    return &Database{}, nil
})

// Global to a package
var Package = do.Package(dbService, ...)

func LazyNamed

func LazyNamed[T any](serviceName string, p Provider[T]) func(Injector)

LazyNamed creates a function that registers a lazy service with a custom name. This function is a convenience wrapper for creating named lazy service registration functions that can be used in packages.

Parameters:

  • serviceName: The custom name for the service
  • p: The provider function that creates the service instance

Returns a function that registers the service as lazy with the specified name when executed.

Example:

dbService := do.LazyNamed[*Database]("main-db", func(i do.Injector) (*Database, error) {
    return &Database{}, nil
})

// Global to a package
var Package = do.Package(dbService)

func MustAs

func MustAs[Initial any, Alias any](i Injector)

MustAs declares an alias for a service and panics if an error occurs. This is a convenience function that wraps As and panics on error.

Parameters:

  • i: The injector to register the alias in

Panics if the alias cannot be created (e.g., type incompatibility or missing service).

Example:

do.MustAs[*PostgresqlDatabase, Database](injector)

func MustAsNamed

func MustAsNamed[Initial any, Alias any](i Injector, initial string, alias string)

MustAsNamed declares a named alias for a named service and panics if an error occurs. This is a convenience function that wraps AsNamed and panics on error.

Parameters:

  • i: The injector to register the alias in
  • initial: The name of the existing service to alias
  • alias: The name for the new alias service

Panics if the alias cannot be created (e.g., type incompatibility or missing service).

Example:

do.MustAsNamed[*PostgresqlDatabase, Database](injector, "my-db", "db-interface")

func MustInvoke

func MustInvoke[T any](i Injector) T

MustInvoke retrieves and instantiates a service from the DI container using type inference. If the service cannot be retrieved or instantiated, it panics.

This function is useful when you're certain the service exists and want to avoid error handling in your code.

Example:

service := do.MustInvoke[*MyService](injector)

func MustInvokeAs

func MustInvokeAs[T any](i Injector) T

MustInvokeAs invokes a service in the DI container by finding the first service that matches the provided type or interface. This function panics if an error occurs during invocation. It's useful when you want to retrieve a service by interface without explicitly creating aliases.

Parameters:

  • i: The injector to search for the service

Returns the service instance. Panics if the service cannot be found or invoked.

Example:

// Register a concrete service
do.Provide(injector, func(i do.Injector) (*PostgresqlDatabase, error) {
    return &PostgresqlDatabase{}, nil
})

// Retrieve by interface
db := do.MustInvokeAs[Database](injector)

func MustInvokeNamed

func MustInvokeNamed[T any](i Injector, name string) T

MustInvokeNamed retrieves and instantiates a named service from the DI container. If the service cannot be retrieved or instantiated, it panics.

This function is useful when you're certain the named service exists and want to avoid error handling in your code.

Example:

service := do.MustInvokeNamed[*MyService](injector, "my-service")

func MustInvokeStruct

func MustInvokeStruct[T any](i Injector) T

MustInvokeStruct invokes services located in struct properties and panics on error. The struct fields must be tagged with `do:""` or `do:"name"`, where `name` is the service name in the DI container. If the service is not found in the DI container, it panics. If the service is found but not assignable to the struct field, it panics.

Example:

type App struct {
    Database *Database `do:""`
    Logger   *Logger   `do:"app-logger"`
    Config   *Config   `do:""`
}

// Register services
do.Provide(injector, func(i do.Injector) (*Database, error) {
    return &Database{}, nil
})
do.ProvideNamed(injector, "app-logger", func(i do.Injector) (*Logger, error) {
    return &Logger{}, nil
})
do.Provide(injector, func(i do.Injector) (*Config, error) {
    return &Config{}, nil
})

// Invoke struct with injected services (panics on error)
app := do.MustInvokeStruct[App](injector)

func MustShutdown

func MustShutdown[T any](i Injector)

MustShutdown stops a service, using type inference to determine the service name. It panics on error. This function performs a graceful shutdown on a service by inferring its name from the type T. If the shutdown fails, this function will panic.

Parameters:

  • i: The injector containing the service

Panics if the shutdown fails.

Example:

do.MustShutdown[*Database](injector)

func MustShutdownNamed

func MustShutdownNamed(i Injector, name string)

MustShutdownNamed stops a named service. It panics on error. This function performs a graceful shutdown on a service with the specified name. If the shutdown fails, this function will panic.

Parameters:

  • i: The injector containing the service
  • name: The name of the service to shutdown

Panics if the shutdown fails.

Example:

do.MustShutdownNamed(injector, "main-database")

func MustShutdownNamedWithContext

func MustShutdownNamedWithContext(ctx context.Context, i Injector, name string)

MustShutdownNamedWithContext stops a named service. It panics on error. This function performs a graceful shutdown on a service with the specified name and context support for timeout and cancellation. If the shutdown fails, this function will panic.

Parameters:

  • ctx: Context for cancellation and timeout control
  • i: The injector containing the service
  • name: The name of the service to shutdown

Panics if the shutdown fails.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

do.MustShutdownNamedWithContext(ctx, injector, "main-database")

func MustShutdownWithContext

func MustShutdownWithContext[T any](ctx context.Context, i Injector)

MustShutdownWithContext stops a service, using type inference to determine the service name. It panics on error. This function performs a graceful shutdown on a service with context support for timeout and cancellation. If the shutdown fails, this function will panic.

Parameters:

  • ctx: Context for cancellation and timeout control
  • i: The injector containing the service

Panics if the shutdown fails.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

do.MustShutdownWithContext[*Database](ctx, injector)

func NameOf

func NameOf[T any]() string

NameOf returns the name of the service in the DI container. This is highly discouraged to use this function, as your code should not declare any dependency explicitly.

The function uses type inference to determine the service name based on the generic type parameter T.

Example:

serviceName := do.NameOf[*Database]()

func Override

func Override[T any](i Injector, provider Provider[T])

Override replaces the service in the DI container, using type inference to determine the service name. Warning: this will not unload/shutdown the previously invoked service.

This function is useful for testing or when you need to replace a service that has already been registered. However, be cautious as it may lead to resource leaks if the original service was already instantiated.

func OverrideNamed

func OverrideNamed[T any](i Injector, name string, provider Provider[T])

OverrideNamed replaces the named service in the DI container. Warning: this will not unload/shutdown the previously invoked service.

This function allows you to replace a specific named service that has already been registered. Use with caution to avoid resource leaks.

func OverrideNamedTransient

func OverrideNamedTransient[T any](i Injector, name string, provider Provider[T])

OverrideNamedTransient replaces the named factory in the DI container. Warning: this will not unload/shutdown the previously invoked service.

This function allows you to replace a specific named transient service factory. Since transient services are recreated on each request, this is generally safer than overriding lazy or eager services.

func OverrideNamedValue

func OverrideNamedValue[T any](i Injector, name string, value T)

OverrideNamedValue replaces the named value in the DI container. Warning: this will not unload/shutdown the previously invoked service.

This function allows you to replace a specific named value service. Use with caution to avoid resource leaks.

func OverrideTransient

func OverrideTransient[T any](i Injector, provider Provider[T])

OverrideTransient replaces the factory in the DI container, using type inference to determine the service name. Warning: this will not unload/shutdown the previously invoked service.

This function replaces an existing transient service factory with a new one. Since transient services are recreated on each request, this is generally safer than overriding lazy or eager services.

func OverrideValue

func OverrideValue[T any](i Injector, value T)

OverrideValue replaces the value in the DI container, using type inference to determine the service name. Warning: this will not unload/shutdown the previously invoked service.

This function replaces an existing value service with a new one. The old value will not be properly cleaned up if it was already instantiated.

func Package

func Package(services ...func(i Injector)) func(Injector)

Package creates a function that executes multiple service registration functions. This function is used to group related service registrations into reusable packages that can be applied to any injector instance.

Parameters:

  • services: Variable number of service registration functions to execute

Returns a function that can be passed to New(), NewWithOpts(), or Scope() to register all services.

Example:

	 // pkg/database/package.go
		// Create a global `Package` variable
		var Package = do.Package(
		    do.Lazy[*Database](func(i do.Injector) (*Database, error) {
		        return &Database{}, nil
		    }),
		    do.Lazy[*ConnectionPool](func(i do.Injector) (*ConnectionPool, error) {
		        return &ConnectionPool{}, nil
		    }),
         ...
		)

	 // main.go
		// Apply the package to an injector
		injector := do.New(database.Package)

func Provide

func Provide[T any](i Injector, provider Provider[T])

Provide registers a service in the DI container, using type inference. The service will be lazily instantiated when first requested.

Example:

do.Provide(injector, func(i do.Injector) (*MyService, error) {
    return &MyService{...}, nil
})

func ProvideNamed

func ProvideNamed[T any](i Injector, name string, provider Provider[T])

ProvideNamed registers a named service in the DI container. This allows you to register multiple services of the same type with different names for disambiguation.

The service will be lazily instantiated when first requested.

Example:

do.ProvideNamed(injector, "main-db", func(i do.Injector) (*Database, error) {
    return &Database{URL: "postgres://main.acme.dev:5432/db"}, nil
})
do.ProvideNamed(injector, "backup-db", func(i do.Injector) (*Database, error) {
    return &Database{URL: "postgres://backup.acme.dev:5432/db"}, nil
})

func ProvideNamedTransient

func ProvideNamedTransient[T any](i Injector, name string, provider Provider[T])

ProvideNamedTransient registers a named factory in the DI container. This allows you to register multiple transient services of the same type with different names for disambiguation.

The service will be recreated each time it is requested, providing a fresh instance.

Example:

// Each invocation creates a new instance
do.ProvideNamedTransient(injector, "request-id", func(i do.Injector) (string, error) {
    return uuid.New().String(), nil
})

// First invocation
id1, _ := do.InvokeNamed[string](injector, "request-id")
// Second invocation - different instance
id2, _ := do.InvokeNamed[string](injector, "request-id")

fmt.Println(id1 != id2) // Output: true

func ProvideNamedValue

func ProvideNamedValue[T any](i Injector, name string, value T)

ProvideNamedValue registers a named value in the DI container. This allows you to register multiple values of the same type with different names for disambiguation.

The value is immediately available and will not be recreated on each request.

Example:

do.ProvideNamedValue(injector, "app-config", &Config{Port: 8080})
do.ProvideNamedValue(injector, "db-config", &Config{Port: 5432})

func ProvideTransient

func ProvideTransient[T any](i Injector, provider Provider[T])

ProvideTransient registers a factory in the DI container, using type inference to determine the service name. The service will be recreated each time it is requested, providing a fresh instance.

Example:

// Each invocation creates a new instance
do.ProvideTransient(injector, func(i do.Injector) (string, error) {
    return uuid.New().String(), nil
})

// First invocation
id1, _ := do.Invoke[string](injector)
// Second invocation - different instance
id2, _ := do.Invoke[string](injector)

fmt.Println(id1 != id2) // Output: true

func ProvideValue

func ProvideValue[T any](i Injector, value T)

ProvideValue registers a value in the DI container, using type inference to determine the service name. The value is immediately available and will not be recreated on each request.

Example:

ProvideValue(injector, &MyService{})

func Shutdown

func Shutdown[T any](i Injector) error

Shutdown stops a service, using type inference to determine the service name. This function performs a graceful shutdown on a service by inferring its name from the type T. The service must implement one of the Shutdowner interfaces.

Parameters:

  • i: The injector containing the service

Returns an error if the shutdown fails, or nil if the shutdown was successful.

Example:

err := do.Shutdown[*Database](injector)
if err != nil {
    log.Printf("Database shutdown failed: %v", err)
}

func ShutdownNamed

func ShutdownNamed(i Injector, name string) error

ShutdownNamed stops a named service. This function performs a graceful shutdown on a service with the specified name. The service must implement one of the Shutdowner interfaces.

Parameters:

  • i: The injector containing the service
  • name: The name of the service to shutdown

Returns an error if the shutdown fails, or nil if the shutdown was successful.

Example:

err := do.ShutdownNamed(injector, "main-database")
if err != nil {
    log.Printf("Main database shutdown failed: %v", err)
}

func ShutdownNamedWithContext

func ShutdownNamedWithContext(ctx context.Context, i Injector, name string) error

ShutdownNamedWithContext stops a named service with context support. This function performs a graceful shutdown on a service with the specified name and context support for timeout and cancellation. The service must implement one of the Shutdowner interfaces.

Parameters:

  • ctx: Context for cancellation and timeout control
  • i: The injector containing the service
  • name: The name of the service to shutdown

Returns an error if the shutdown fails, or nil if the shutdown was successful.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

err := do.ShutdownNamedWithContext(ctx, injector, "main-database")
if err != nil {
    log.Printf("Main database shutdown failed: %v", err)
}

func ShutdownWithContext

func ShutdownWithContext[T any](ctx context.Context, i Injector) error

ShutdownWithContext stops a service, using type inference to determine the service name. This function performs a graceful shutdown on a service with context support for timeout and cancellation. The service must implement one of the Shutdowner interfaces.

Parameters:

  • ctx: Context for cancellation and timeout control
  • i: The injector containing the service

Returns an error if the shutdown fails, or nil if the shutdown was successful.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

err := do.ShutdownWithContext[*Database](ctx, injector)
if err != nil {
    log.Printf("Database shutdown failed: %v", err)
}

func Transient

func Transient[T any](p Provider[T]) func(Injector)

Transient creates a function that registers a transient service using the default service name. This function is a convenience wrapper for creating transient service registration functions that can be used in packages.

Parameters:

  • p: The provider function that creates the service instance

Returns a function that registers the service as transient when executed.

Example:

loggerService := do.Transient[*Logger](func(i do.Injector) (*Logger, error) {
    return &Logger{}, nil
})

// Global to a package
var Package = do.Package(loggerService, ...)

func TransientNamed

func TransientNamed[T any](serviceName string, p Provider[T]) func(Injector)

TransientNamed creates a function that registers a transient service with a custom name. This function is a convenience wrapper for creating named transient service registration functions that can be used in packages.

Parameters:

  • serviceName: The custom name for the service
  • p: The provider function that creates the service instance

Returns a function that registers the service as transient with the specified name when executed.

Example:

loggerService := do.TransientNamed[*Logger]("request-logger", func(i do.Injector) (*Logger, error) {
    return &Logger{}, nil
})

// Global to a package
var Package = do.Package(loggerService, ...)

Types

type DAG

type DAG struct {
	// contains filtered or unexported fields
}

DAG represents a Directed Acyclic Graph of services, tracking dependencies and dependents. This type manages the relationships between services to ensure proper initialization order and detect circular dependencies.

The DAG maintains two maps:

  • dependencies: Maps each service to the services it depends on
  • dependents: Maps each service to the services that depend on it

Fields:

  • mu: Read-write mutex for thread-safe access to the graph
  • dependencies: Map of services to their dependencies
  • dependents: Map of services to their dependents

type ExplainInjectorOutput

type ExplainInjectorOutput struct {
	ScopeID   string                       `json:"scope_id"`
	ScopeName string                       `json:"scope_name"`
	DAG       []ExplainInjectorScopeOutput `json:"dag"`
}

ExplainInjectorOutput contains detailed information about an injector and its scope hierarchy. This struct provides a comprehensive view of the injector's scope tree, including all services and their relationships.

func ExplainInjector

func ExplainInjector(scope Injector) ExplainInjectorOutput

ExplainInjector returns a human readable description of the injector, with services and scope tree. This function provides a comprehensive view of the injector's structure, including all scopes, services, and their relationships in a hierarchical format.

Parameters:

  • scope: The injector to explain

Returns a detailed explanation of the injector's structure and contents.

Example:

explanation := do.ExplainInjector(injector)
fmt.Println(explanation.String())

Output example:

Scope ID: root
Scope name: Root Scope

DAG:
 |\_ Root Scope (ID: root)
 |   * 😴 Database
 |   * 🔁 Config
 |   |\_ API Scope (ID: api)
 |   |   * 🏭 Logger
 |   |   * 🔗 DatabaseInterface

func (*ExplainInjectorOutput) String

func (id *ExplainInjectorOutput) String() string

String returns a formatted string representation of the injector explanation. This method provides a human-readable description of the injector including its scope hierarchy and all services in a tree-like format.

Returns a formatted string describing the injector and its scope hierarchy.

type ExplainInjectorScopeOutput

type ExplainInjectorScopeOutput struct {
	ScopeID   string                         `json:"scope_id"`
	ScopeName string                         `json:"scope_name"`
	Scope     Injector                       `json:"scope"`
	Services  []ExplainInjectorServiceOutput `json:"services"`
	Children  []ExplainInjectorScopeOutput   `json:"children"`

	IsAncestor bool `json:"is_ancestor"`
	IsChildren bool `json:"is_children"`
}

ExplainInjectorScopeOutput contains information about a single scope in the injector hierarchy. This struct represents a scope and its services, along with its position in the scope tree.

func (*ExplainInjectorScopeOutput) String

func (ids *ExplainInjectorScopeOutput) String() string

String returns a formatted string representation of the scope. This method provides a human-readable description of the scope including its services and child scopes.

Returns a formatted string describing the scope.

type ExplainInjectorServiceOutput

type ExplainInjectorServiceOutput struct {
	ServiceName      string        `json:"service_name"`
	ServiceType      ServiceType   `json:"service_type"`
	ServiceTypeIcon  string        `json:"service_type_icon"`
	ServiceBuildTime time.Duration `json:"service_build_time,omitempty"`
	IsHealthchecker  bool          `json:"is_healthchecker"`
	IsShutdowner     bool          `json:"is_shutdowner"`
}

ExplainInjectorServiceOutput contains information about a service in the scope explanation. This struct provides details about a service's type, capabilities, and lifecycle state.

func (*ExplainInjectorServiceOutput) String

func (idss *ExplainInjectorServiceOutput) String() string

String returns a formatted string representation of the service. This method provides a human-readable description of the service including its type icon and capabilities indicators.

Returns a formatted string describing the service.

type ExplainServiceDependencyOutput

type ExplainServiceDependencyOutput struct {
	ScopeID   string                           `json:"scope_id"`
	ScopeName string                           `json:"scope_name"`
	Service   string                           `json:"service"`
	Recursive []ExplainServiceDependencyOutput `json:"recursive"`
}

ExplainServiceDependencyOutput contains information about a service dependency relationship. This struct represents either a dependency (service this depends on) or a dependent (service that depends on this) in the dependency graph.

Fields:

  • ScopeID: The unique identifier of the scope containing the related service
  • ScopeName: The human-readable name of the scope containing the related service
  • Service: The name of the related service
  • Recursive: Nested dependency relationships (for transitive dependencies)

func (*ExplainServiceDependencyOutput) String

func (sdd *ExplainServiceDependencyOutput) String() string

String returns a formatted string representation of the dependency relationship. This method provides a human-readable description of the dependency, including nested recursive dependencies with proper indentation.

Returns a formatted string describing the dependency relationship.

type ExplainServiceOutput

type ExplainServiceOutput struct {
	ScopeID          string                           `json:"scope_id"`
	ScopeName        string                           `json:"scope_name"`
	ServiceName      string                           `json:"service_name"`
	ServiceType      ServiceType                      `json:"service_type"`
	ServiceBuildTime time.Duration                    `json:"service_build_time,omitempty"`
	Invoked          *stacktrace.Frame                `json:"invoked"`
	Dependencies     []ExplainServiceDependencyOutput `json:"dependencies"`
	Dependents       []ExplainServiceDependencyOutput `json:"dependents"`
}

ExplainServiceOutput contains detailed information about a service for debugging and analysis. This struct provides comprehensive information about a service's location, type, dependencies, and lifecycle state.

func ExplainNamedService

func ExplainNamedService(scope Injector, name string) (description ExplainServiceOutput, ok bool)

ExplainNamedService returns a human readable description of the service by name. This function provides detailed information about a service including its scope, type, dependencies, and lifecycle state. The service must be registered before calling this function.

Parameters:

  • scope: The injector to search for the service
  • name: The name of the service to explain

Returns a service explanation and a boolean indicating if the service was found. The boolean is false if the service is not found.

Note: Please call Invoke[T] before ExplainNamedService[T] to ensure that the service is registered.

Example:

// First invoke the service to ensure it's registered
	db := do.MustInvokeNamed[*Database](injector, "main-db")

// Then explain it
	explanation, found := do.ExplainNamedService(injector, "main-db")
	if found {
	    fmt.Println(explanation.String())
	}

func ExplainService

func ExplainService[T any](i Injector) (description ExplainServiceOutput, ok bool)

ExplainService returns a human readable description of the service. This function provides detailed information about a service including its scope, type, dependencies, and lifecycle state. The service must be registered before calling this function.

Parameters:

  • i: The injector to search for the service

Returns a service explanation and a boolean indicating if the service was found. The boolean is false if the service is not found.

Note: Please call Invoke[T] before ExplainService[T] to ensure that the service is registered.

Example:

// First invoke the service to ensure it's registered
	db := do.MustInvoke[*Database](injector)

// Then explain it
	explanation, found := do.ExplainService[*Database](injector)
	if found {
		fmt.Println(explanation.String())
	}

func (*ExplainServiceOutput) String

func (sd *ExplainServiceOutput) String() string

String returns a formatted string representation of the service explanation. This method provides a human-readable description of the service including its scope, type, build time, and dependency relationships.

Returns a formatted string describing the service.

type Healthchecker

type Healthchecker interface {
	HealthCheck() error
}

Healthchecker is an interface that services can implement to provide health checking capabilities. Services implementing this interface can be health-checked by the DI container to ensure they are functioning correctly.

The HealthCheck method should perform a quick check to verify the service is healthy. It should return nil if the service is healthy, or an error describing the health issue.

Example:

type Database struct {
    conn *sql.DB
}

func (db *Database) HealthCheck() error {
    return db.conn.Ping()
}

type HealthcheckerWithContext

type HealthcheckerWithContext interface {
	HealthCheck(context.Context) error
}

HealthcheckerWithContext is an interface that services can implement to provide health checking capabilities with context support. This allows for timeout and cancellation control during health checks.

The HealthCheck method should perform a quick check to verify the service is healthy. It should respect the provided context for cancellation and timeout. It should return nil if the service is healthy, or an error describing the health issue.

Example:

type Database struct {
    conn *sql.DB
}

func (db *Database) HealthCheck(ctx context.Context) error {
    return db.conn.PingContext(ctx)
}

type Injector

type Injector interface {

	// ID returns the unique identifier of the injector.
	ID() string

	// Name returns the human-readable name of the injector.
	Name() string

	// Scope creates a new child scope with the given name.
	// Optional package functions can be provided to execute during scope creation.
	Scope(string, ...func(Injector)) *Scope

	// RootScope returns the root scope of the injector hierarchy.
	RootScope() *RootScope

	// Ancestors returns the list of all parent scopes in order from immediate parent to root.
	Ancestors() []*Scope

	// Children returns the list of immediate child scopes.
	Children() []*Scope

	// ChildByID searches for a child scope by its unique ID across the entire scope hierarchy.
	ChildByID(string) (*Scope, bool)

	// ChildByName searches for a child scope by its name across the entire scope hierarchy.
	ChildByName(string) (*Scope, bool)

	// ListProvidedServices returns all services available in the current scope and all its ancestor scopes.
	ListProvidedServices() []ServiceDescription

	// ListInvokedServices returns only the services that have been actually invoked in the current scope and its ancestors.
	ListInvokedServices() []ServiceDescription

	// HealthCheck performs health checks on all services in the current scope and its ancestors.
	HealthCheck() map[string]error

	// HealthCheckWithContext performs health checks with context support for cancellation and timeouts.
	HealthCheckWithContext(context.Context) map[string]error

	// Shutdown gracefully shuts down the injector and all its descendant scopes.
	Shutdown() *ShutdownReport

	// ShutdownWithContext gracefully shuts down the injector and all its descendant scopes with context support.
	ShutdownWithContext(context.Context) *ShutdownReport
	// contains filtered or unexported methods
}

Injector is the main interface for dependency injection containers. It provides methods for service registration, resolution, lifecycle management, and scope hierarchy management.

The Injector interface is implemented by both RootScope and Scope types, allowing for a consistent API across different levels of the scope hierarchy.

type InjectorOpts

type InjectorOpts struct {
	// HookBeforeRegistration is called before a service is registered in a scope.
	// This hook can be used for validation, logging, or other pre-registration tasks.
	HookBeforeRegistration []func(scope *Scope, serviceName string)

	// HookAfterRegistration is called after a service is successfully registered in a scope.
	// This hook can be used for logging, metrics collection, or other post-registration tasks.
	HookAfterRegistration []func(scope *Scope, serviceName string)

	// HookBeforeInvocation is called before a service is invoked (instantiated).
	// This hook can be used for logging, metrics, or other pre-invocation tasks.
	HookBeforeInvocation []func(scope *Scope, serviceName string)

	// HookAfterInvocation is called after a service is invoked, with the result error.
	// This hook can be used for logging, metrics collection, or error handling.
	HookAfterInvocation []func(scope *Scope, serviceName string, err error)

	// HookBeforeShutdown is called before a service is shut down.
	// This hook can be used for cleanup preparation or logging.
	HookBeforeShutdown []func(scope *Scope, serviceName string)

	// HookAfterShutdown is called after a service is shut down, with the result error.
	// This hook can be used for logging, metrics collection, or error handling.
	HookAfterShutdown []func(scope *Scope, serviceName string, err error)

	// Logf is the logging function used by the DI container for internal logging.
	// If not provided, no logging will occur. This function should handle the format
	// string and arguments similar to fmt.Printf.
	Logf func(format string, args ...any)

	// HealthCheckParallelism controls the number of concurrent health checks that can run simultaneously.
	// Default: all health checks run in parallel (unlimited).
	// Setting this to a positive number limits the concurrency for better resource management.
	HealthCheckParallelism uint

	// HealthCheckGlobalTimeout sets a global timeout for all health check operations.
	// This timeout applies to the entire health check process across all services.
	// Default: no timeout (health checks can run indefinitely).
	HealthCheckGlobalTimeout time.Duration

	// HealthCheckTimeout sets a timeout for individual service health checks.
	// This timeout applies to each service's health check method.
	// Default: no timeout (individual health checks can run indefinitely).
	HealthCheckTimeout time.Duration

	// StructTagKey specifies the tag key used for struct field injection.
	// Default: "do" (see DefaultStructTagKey constant).
	// This allows customization of the struct tag format for injection.
	StructTagKey string
}

InjectorOpts contains all configuration options for the dependency injection container. These options control logging, hooks, health checks, and other behavioral aspects of the DI container.

type Provider

type Provider[T any] func(Injector) (T, error)

Provider is a function type that creates and returns a service instance of type T. This is the core abstraction for service creation in the dependency injection container.

The provider function receives an Injector instance that can be used to resolve dependencies for the service being created.

Example:

func NewMyService(i do.Injector) (*MyService, error) {
    db := do.MustInvoke[*Database](i)
    config := do.MustInvoke[*Config](i)
    return &MyService{DB: db, Config: config}, nil
}

// Register the provider
do.Provide(injector, NewMyService)

type RootScope

type RootScope struct {
	// contains filtered or unexported fields
}

RootScope is the top-level scope in the dependency injection container hierarchy. It serves as the entry point for all DI operations and manages the overall container lifecycle.

Key responsibilities:

  • Service registration and resolution
  • Child scope management
  • Health check coordination
  • Shutdown orchestration
  • Dependency graph management

func New

func New(packages ...func(Injector)) *RootScope

New creates a new dependency injection container with default options. This is the primary entry point for creating a new DI container.

Parameters:

  • packages: Optional package functions to execute during initialization

Returns a new RootScope instance ready for service registration.

Example:

injector := do.New()
do.Provide(injector, func(i do.Injector) (*MyService, error) {
    return &MyService{}, nil
})

func NewWithOpts

func NewWithOpts(opts *InjectorOpts, packages ...func(Injector)) *RootScope

NewWithOpts creates a new dependency injection container with custom options. This allows you to configure logging, hooks, health check settings, and other options.

Parameters:

  • opts: Configuration options for the injector
  • packages: Optional package functions to execute during initialization

Returns a new RootScope instance with the specified configuration.

Example:

opts := &do.InjectorOpts{
    Logf: func(format string, args ...any) {
        log.Printf(format, args...)
    },
    HealthCheckParallelism: 10,
}
injector := do.NewWithOpts(opts)

func (*RootScope) AddAfterInvocationHook

func (s *RootScope) AddAfterInvocationHook(hook func(*Scope, string, error))

AddAfterInvocationHook adds a hook that will be called after a service is invoked.

func (*RootScope) AddAfterRegistrationHook

func (s *RootScope) AddAfterRegistrationHook(hook func(*Scope, string))

AddAfterRegistrationHook adds a hook that will be called after a service is registered.

func (*RootScope) AddAfterShutdownHook

func (s *RootScope) AddAfterShutdownHook(hook func(*Scope, string, error))

AddAfterShutdownHook adds a hook that will be called after a service is shutdown.

func (*RootScope) AddBeforeInvocationHook

func (s *RootScope) AddBeforeInvocationHook(hook func(*Scope, string))

AddBeforeInvocationHook adds a hook that will be called before a service is invoked.

func (*RootScope) AddBeforeRegistrationHook

func (s *RootScope) AddBeforeRegistrationHook(hook func(*Scope, string))

AddBeforeRegistrationHook adds a hook that will be called before a service is registered.

func (*RootScope) AddBeforeShutdownHook

func (s *RootScope) AddBeforeShutdownHook(hook func(*Scope, string))

AddBeforeShutdownHook adds a hook that will be called before a service is shutdown.

func (*RootScope) Ancestors

func (s *RootScope) Ancestors() []*Scope

Ancestors returns an empty slice since the root scope has no ancestors.

func (*RootScope) ChildByID

func (s *RootScope) ChildByID(id string) (*Scope, bool)

ChildByID searches for a child scope by its unique ID across the entire scope hierarchy.

func (*RootScope) ChildByName

func (s *RootScope) ChildByName(name string) (*Scope, bool)

ChildByName searches for a child scope by its name across the entire scope hierarchy.

func (*RootScope) Children

func (s *RootScope) Children() []*Scope

Children returns the list of immediate child scopes.

func (*RootScope) Clone

func (s *RootScope) Clone() *RootScope

Clone clones injector with provided services but not with invoked instances.

func (*RootScope) CloneWithOpts

func (s *RootScope) CloneWithOpts(opts *InjectorOpts) *RootScope

CloneWithOpts clones injector with provided services but not with invoked instances, with options.

func (*RootScope) HealthCheck

func (s *RootScope) HealthCheck() map[string]error

HealthCheck performs health checks on all services in the root scope and all its descendant scopes.

func (*RootScope) HealthCheckWithContext

func (s *RootScope) HealthCheckWithContext(ctx context.Context) map[string]error

HealthCheckWithContext performs health checks with context support for cancellation and timeouts.

func (*RootScope) ID

func (s *RootScope) ID() string

ID returns the unique identifier of the root scope.

func (*RootScope) ListInvokedServices

func (s *RootScope) ListInvokedServices() []ServiceDescription

ListInvokedServices returns all services that have been invoked in the root scope and all its descendant scopes.

func (*RootScope) ListProvidedServices

func (s *RootScope) ListProvidedServices() []ServiceDescription

ListProvidedServices returns all services available in the root scope and all its descendant scopes.

func (*RootScope) Name

func (s *RootScope) Name() string

Name returns the name of the root scope.

func (*RootScope) RootScope

func (s *RootScope) RootScope() *RootScope

RootScope returns the root scope itself (this instance).

func (*RootScope) Scope

func (s *RootScope) Scope(name string, p ...func(Injector)) *Scope

Scope creates a new child scope under the root scope.

func (*RootScope) Shutdown

func (s *RootScope) Shutdown() *ShutdownReport

Shutdown gracefully shuts down the root scope and all its descendant scopes.

func (*RootScope) ShutdownOnSignals

func (s *RootScope) ShutdownOnSignals(signals ...os.Signal) (os.Signal, *ShutdownReport)

ShutdownOnSignals listens for the provided signals in order to gracefully stop services. It will block until receiving any of these signals. If no signal is provided, syscall.SIGTERM and os.Interrupt will be handled by default.

func (*RootScope) ShutdownOnSignalsWithContext

func (s *RootScope) ShutdownOnSignalsWithContext(ctx context.Context, signals ...os.Signal) (os.Signal, *ShutdownReport)

ShutdownOnSignalsWithContext listens for the provided signals in order to gracefully stop services. It will block until receiving any of these signals. If no signal is provided, syscall.SIGTERM and os.Interrupt will be handled by default.

func (*RootScope) ShutdownWithContext

func (s *RootScope) ShutdownWithContext(ctx context.Context) *ShutdownReport

ShutdownWithContext gracefully shuts down the root scope and all its descendant scopes with context support. This method ensures proper cleanup of the health check pool and all registered services.

type Scope

type Scope struct {
	// contains filtered or unexported fields
}

Scope represents a dependency injection container that can contain services and child scopes. Scopes form a hierarchical tree structure where child scopes can access services from their parent scopes, but not vice versa.

Key features:

  • Hierarchical service resolution (child scopes can access parent services)
  • Isolated service registration (services in child scopes don't affect parents)
  • Thread-safe operations
  • Service lifecycle management (health checks, shutdown)
  • Observability and debugging support

func (*Scope) Ancestors

func (s *Scope) Ancestors() []*Scope

Ancestors returns the list of all parent scopes in order from immediate parent to root. This is useful for understanding the scope hierarchy and for operations that need to traverse up the scope tree.

Returns an empty slice for the root scope, and a slice of parent scopes for child scopes, ordered from immediate parent to root.

func (*Scope) ChildByID

func (s *Scope) ChildByID(id string) (*Scope, bool)

ChildByID searches for a child scope by its unique ID across the entire scope hierarchy. This method performs a recursive search through all descendant scopes.

Parameters:

  • id: The unique ID of the scope to find

Returns the found scope and true if found, or nil and false if not found.

func (*Scope) ChildByName

func (s *Scope) ChildByName(name string) (*Scope, bool)

ChildByName searches for a child scope by its name across the entire scope hierarchy. This method performs a recursive search through all descendant scopes.

Parameters:

  • name: The name of the scope to find

Returns the found scope and true if found, or nil and false if not found.

func (*Scope) Children

func (s *Scope) Children() []*Scope

Children returns the list of immediate child scopes. This method only returns direct children, not grandchildren or deeper descendants.

Returns a slice of child scopes. The order is not guaranteed to be stable.

func (*Scope) HealthCheck

func (s *Scope) HealthCheck() map[string]error

HealthCheck performs health checks on all services in the current scope and its ancestors that implement the Healthchecker interface.

Returns a map of service names to error values. A nil error indicates a successful health check.

func (*Scope) HealthCheckWithContext

func (s *Scope) HealthCheckWithContext(ctx context.Context) map[string]error

HealthCheckWithContext performs health checks on all services in the current scope and its ancestors that implement the Healthchecker interface, with context support for cancellation and timeouts.

Parameters:

  • ctx: Context for cancellation and timeout control

Returns a map of service names to error values. A nil error indicates a successful health check.

func (*Scope) ID

func (s *Scope) ID() string

ID returns the unique identifier of the scope. This ID is generated using UUID and is immutable throughout the scope's lifetime.

func (*Scope) ListInvokedServices

func (s *Scope) ListInvokedServices() []ServiceDescription

ListInvokedServices returns only the services that have been actually invoked (instantiated) in the current scope and its ancestors. This is useful for understanding which services are actually being used and for debugging dependency issues.

Returns a slice of ServiceDescription objects representing only the invoked services.

func (*Scope) ListProvidedServices

func (s *Scope) ListProvidedServices() []ServiceDescription

ListProvidedServices returns all services available in the current scope and all its ancestor scopes. This provides a complete view of the service hierarchy, showing all services that can be accessed from the current scope.

Returns a slice of ServiceDescription objects representing all available services, including those inherited from parent scopes.

func (*Scope) Name

func (s *Scope) Name() string

Name returns the human-readable name of the scope. This name is provided when creating the scope and is immutable.

func (*Scope) RootScope

func (s *Scope) RootScope() *RootScope

RootScope returns the root scope of the scope hierarchy. All scopes in a hierarchy share the same root scope, regardless of their depth.

func (*Scope) Scope

func (s *Scope) Scope(name string, packages ...func(Injector)) *Scope

Scope creates a new child scope with the given name. Child scopes inherit access to services from their parent scopes, but services registered in child scopes are not accessible to parents.

Parameters:

  • name: The name for the new child scope (must be unique within the parent)
  • packages: Optional package functions to execute in the new scope

Returns the newly created child scope.

Panics if a scope with the same name already exists in the parent.

func (*Scope) Shutdown

func (s *Scope) Shutdown() *ShutdownReport

Shutdown gracefully shuts down the scope and all its children. This method calls ShutdownWithContext with a background context.

Returns a ShutdownReport containing any errors and timings that occurred during shutdown.

func (*Scope) ShutdownWithContext

func (s *Scope) ShutdownWithContext(ctx context.Context) *ShutdownReport

ShutdownWithContext gracefully shuts down the scope and all its children with context support. This method performs shutdown operations in parallel for better performance.

Parameters:

  • ctx: Context for cancellation and timeout control

Returns a ShutdownReport containing any errors and timings that occurred during shutdown.

type ServiceDescription

type ServiceDescription struct {
	ScopeID   string
	ScopeName string
	Service   string
}

ServiceDescription represents a service in the dependency graph (DAG), identified by scope ID, scope name, and service name. This type is used to uniquely identify services across the entire scope hierarchy for dependency tracking.

Fields:

  • ScopeID: The unique identifier of the scope containing the service
  • ScopeName: The human-readable name of the scope containing the service
  • Service: The name of the service within the scope

type ServiceType

type ServiceType string

ServiceType represents the different types of services that can be registered in the dependency injection container. Each type has different lifecycle and instantiation behavior.

const (
	// ServiceTypeLazy represents a service that is instantiated only when first requested.
	// The service instance is cached and reused for subsequent requests.
	ServiceTypeLazy ServiceType = "lazy"

	// ServiceTypeEager represents a service that is instantiated immediately when registered.
	// The service is always available and ready to use.
	ServiceTypeEager ServiceType = "eager"

	// ServiceTypeTransient represents a service that is recreated each time it is requested.
	// No singleton caching is performed, ensuring a fresh instance every time. It is basically a factory.
	ServiceTypeTransient ServiceType = "transient"

	// ServiceTypeAlias represents a service that is an alias to another service.
	// It provides a different interface or name for accessing an existing service.
	ServiceTypeAlias ServiceType = "alias"
)

type ShutdownReport

type ShutdownReport struct {
	Succeed             bool
	Services            []ServiceDescription
	Errors              map[ServiceDescription]error
	ShutdownTime        time.Duration
	ServiceShutdownTime map[ServiceDescription]time.Duration
}

ShutdownReport represents the result of a shutdown operation. It includes overall success, the list of services that were shut down, any errors encountered, total shutdown time, and per-service shutdown durations.

It implements the error interface, returning a formatted description of errors when any occurred, or a "no shutdown errors" message otherwise.

func (ShutdownReport) Error

func (r ShutdownReport) Error() string

Error implements the error interface for ShutdownReport. If there are errors, it returns a multiline description. Otherwise a friendly message.

type Shutdowner

type Shutdowner interface {
	Shutdown()
}

Shutdowner is an interface that services can implement to provide graceful shutdown capabilities. Services implementing this interface will be called during container shutdown to perform cleanup operations.

The Shutdown method should perform any necessary cleanup, such as closing connections, flushing buffers, or stopping background processes.

Example:

type Logger struct {
    file *os.File
}

func (l *Logger) Shutdown() {
    l.file.Close()
}

type ShutdownerWithContext

type ShutdownerWithContext interface {
	Shutdown(context.Context)
}

ShutdownerWithContext is an interface that services can implement to provide graceful shutdown capabilities with context support. This allows for timeout and cancellation control during shutdown operations.

The Shutdown method should perform any necessary cleanup and respect the provided context for cancellation and timeout.

Example:

type Server struct {
    srv *http.Server
}

func (s *Server) Shutdown(ctx context.Context) {
    s.srv.Shutdown(ctx)
}

type ShutdownerWithContextAndError

type ShutdownerWithContextAndError interface {
	Shutdown(context.Context) error
}

ShutdownerWithContextAndError is an interface that services can implement to provide graceful shutdown capabilities with both context support and error reporting. This is the most flexible shutdown interface, allowing for timeout control and error reporting.

The Shutdown method should perform any necessary cleanup, respect the provided context for cancellation and timeout, and return an error if the shutdown process fails.

Example:

type Server struct {
    srv *http.Server
}

func (s *Server) Shutdown(ctx context.Context) error {
    return s.srv.Shutdown(ctx)
}

type ShutdownerWithError

type ShutdownerWithError interface {
	Shutdown() error
}

ShutdownerWithError is an interface that services can implement to provide graceful shutdown capabilities with error reporting. This allows services to report any errors that occur during shutdown.

The Shutdown method should perform any necessary cleanup and return an error if the shutdown process fails.

Example:

type Database struct {
    conn *sql.DB
}

func (db *Database) Shutdown() error {
    return db.conn.Close()
}

Directories

Path Synopsis