dig

repository·master·Indexed 26 days ago

https://github.com/uber-go/dig

A reflection-based dependency injection toolkit for Go designed to resolve object graphs during application startup. It provides features for managing dependency containers, creating child scopes, decorating types, and handling complex dependency requirements using Parameter Objects (dig.In) and Result Objects (dig.Out). The toolkit includes utilities for detecting circular dependencies, capturing panics, and visualizing dependency graphs via DOT files and Graphviz.

Tokens
5.8K
Snippets
13
Records
48
Agent score
88%

What's inside dig

  1. Overview of dig

    master

    dig is a reflection-based dependency injection toolkit for Go.

    Best Use Cases:

    • Powering an application framework (e.g., Fx).
    • Resolving the object graph during process startup.

    When NOT to use dig:

    • As a replacement for a full application framework.
    • For resolving dependencies after the process has already started.
    • As a Service Locator in user-land code.
  2. Generate DOT file representations of dependency graphs

    master

    The dot module can be used to generate a DOT file representation of a dependency graph. This is useful for visualizing how constructors, results, and groups interact within the dependency injection container.

    Interpreting the Graph

    Nodes:

    • Constructors (Rectangles): Take parameters and produce results.
    • Results (Ovals): Produced by a constructor. They are consumed by other constructors or as part of a group.
    • Groups (Diamonds): Represent value groups. Multiple results can form a group. A group is a collection of results and can be used as a parameter for constructors.

    Edges:

    • Solid Arrows: Indicate a direct dependency (node_a depends on node_b, and node_b is a parameter of node_a).
    • Dashed Arrows: Indicate an optional dependency.

    Colors (Error States):

    • Red: The node is the root cause of a failure.
    • Orange: The node is a transitive failure.
  3. Visualize dependency graphs as PNG images

    master

    To visualize the effect of code changes, you can generate DOT files and then convert them to PNG images using graphviz.

    1. Generate the DOT files in the dig root directory:
      go test -generate
    2. Navigate to the testdata directory.
    3. Convert the .dot file to a .png using the dot command (requires graphviz):
      dot -Tpng ${name_of_dot_file_in_testdata}.dot -o ${name_of_dot_file_in_testdata}.png
      open ${name_of_dot_file_in_testdata}.png
    $ go test -generate
    $ dot -Tpng ${name_of_dot_file_in_testdata}.dot -o ${name_of_dot_file_in_testdata}.png
  4. Install dig

    master

    Install the v1 major version of dig using your preferred Go dependency manager. It is recommended to use SemVer major version 1.

    $ glide get 'go.uber.org/dig#^1'
    $ dep ensure -add "go.uber.org/dig@v1"
    $ go get 'go.uber.org/dig@v1'
  5. Understand dependency parameter types in dig

    master

    In dig, dependencies for constructors are represented by the param interface. When you provide a constructor to a container, dig identifies its dependencies using one of the following internal parameter implementations:

    • paramSingle: An explicitly requested type (the most common case).
    • paramObject: A dig.In struct where each field is treated as an individual dependency.
    • paramGroupedSlice: A slice that consumes all values produced with a group:".." tag sharing the same group name.
    • paramList: Represents all arguments of a constructor (used internally to build the list of arguments).
  6. Use dig.In structs for dependency injection

    master

    Instead of listing many arguments in a constructor, you can use a struct that embeds dig.In. Each exported field in the struct becomes a dependency that dig will attempt to satisfy.

    Rules for dig.In structs:

    • The struct must embed dig.In.
    • Fields must be exported (start with an uppercase letter).
    • You cannot embed *dig.In (the pointer version); use the value type instead.
    • You cannot depend on a pointer to a dig.In struct; use the value type.
    • You can use the name:"..." tag to request a specific named dependency.
    • You can use the optional tag to make a field dependency optional.
    • You can use the group:"..." tag to consume a group of values into a slice.
    • You can use the ignoreUnexported:"true" tag on the dig.In field to allow dig to ignore unexported fields in the struct.
  7. Handle invalid group options in dig

    master
    When configuring dependency groups using string tags, if an unrecognized option is provided, dig will return an error. The error indicates which specific option was invalid. Valid options for group strings are flatten and soft.
  8. Detect circular dependencies with IsCycleDetected

    master
    When building a dependency graph with dig, you may encounter circular dependencies (cycles). You can programmatically check if an error returned by the container is caused by a cycle using the IsCycleDetected function. This is useful for providing specific error handling or logging when the object graph cannot be resolved due to a loop.
  9. Request named or optional dependencies

    master

    When using a dig.In struct, you can refine how dependencies are matched using struct tags:

    • name:"foo": Matches a dependency that was provided with the name "foo".
    • optional: If the dependency is not found in the container, dig will provide the zero value for that type instead of returning an error.

    Example:

    type MyDeps struct {
    	dig.In
    	// Requests a specific named dependency
    	Logger Logger `name:"audit_logger"` 
    
    	// If this dependency is missing, it won't cause an error
    	Config *Config `optional` 
    }
  10. Consume value groups with slices

    master

    You can collect multiple values of the same type into a single slice by using the group tag. This is useful for plugin architectures or collecting multiple implementations of an interface.

    To use this, define a field in a dig.In struct as a slice and tag it with group:"<name>".

    Example:

    type MyConfig struct {
    	dig.In
    	Plugins []Plugin `group:"plugins"` // Consumes all values in the "plugins" group
    }
  11. Register a callback for a decorator with WithDecoratorCallback

    master

    Use WithDecoratorCallback to register a Callback that executes after a decorator finishes running. The callback receives a CallbackInfo object containing the function's name, any error returned, and the execution duration.

    This is useful for monitoring the execution of decorators used to wrap or modify existing providers.

    c := dig.New()
    myCallback := func(ci CallbackInfo) {
    	var errorAdd string
    	if ci.Error != nil {
    		errorAdd = fmt.Sprintf("with error: %v", ci.Error)
    	}
    	fmt.Printf("%q finished%v", ci.Name, errorAdd)
    }
    c.Decorate(myDecorator, WithDecoratorCallback(myCallback))