dig

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 15, 2017 License: MIT Imports: 6 Imported by: 0

README

🔨 dig GoDoc GitHub release Build Status Coverage Status Go Report Card

A reflection based dependency injection toolkit for Go.

Good for:
  • Powering an application framework, e.g. Fx.
  • Resolving the object graph during process startup.
Bad for:
  • Using in place of an application framework, e.g. Fx.
  • Resolving dependencies after the process has already started.
  • Exposing to user-land code as a Service Locator.

Installation

We recommend locking to SemVer range ^1 using Glide:

glide get 'go.uber.org/dig#^1'

Stability

This library is v1 and follows SemVer strictly.

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

Documentation

Overview

Package dig provides an opinionated way of resolving object dependencies.

Status

STABLE. No breaking changes will be made in this major version.

Container

Dig exposes type Container as an object capable of resolving a directed acyclic dependency graph. Use the New function to create one.

c := dig.New()

Provide

Constructors for different types are added to the container by using the Provide method. A constructor can declare a dependency on another type by simply adding it as a function parameter. Dependencies for a type can be added to the graph both, before and after the type was added.

err := c.Provide(func(conn *sql.DB) (*UserGateway, error) {
  // ...
})
if err != nil {
  // ...
}

if err := c.Provide(newDBConnection); err != nil {
  // ...
}

Multiple constructors can rely on the same type. The container creates a singleton for each retained type, instantiating it at most once when requested directly or as a dependency of another type.

err := c.Provide(func(conn *sql.DB) *CommentGateway {
  // ...
})
if err != nil {
  // ...
}

Constructors can declare any number of dependencies as parameters and optionally, return errors.

err := c.Provide(func(u *UserGateway, c *CommentGateway) (*RequestHandler, error) {
  // ...
})
if err != nil {
  // ...
}

if err := c.Provide(newHTTPServer); err != nil {
  // ...
}

Constructors can also return multiple results to add multiple types to the container.

err := c.Provide(func(conn *sql.DB) (*UserGateway, *CommentGateway, error) {
  // ...
})
if err != nil {
  // ...
}

Constructors that accept a variadic number of arguments are treated as if they don't have those arguments. That is,

func NewVoteGateway(db *sql.DB, options ...Option) *VoteGateway

Is treated the same as,

func NewVoteGateway(db *sql.DB) *VoteGateway

The constructor will be called with all other dependencies and no variadic arguments.

Invoke

Types added to to the container may be consumed by using the Invoke method. Invoke accepts any function that accepts one or more parameters and optionally, returns an error. Dig calls the function with the requested type, instantiating only those types that were requested by the function. The call fails if any type or its dependencies (both direct and transitive) were not available in the container.

err := c.Invoke(func(l *log.Logger) {
  // ...
})
if err != nil {
  // ...
}

err := c.Invoke(func(server *http.Server) error {
  // ...
})
if err != nil {
  // ...
}

Any error returned by the invoked function is propagated back to the caller.

Parameter Objects

Constructors declare their dependencies as function parameters. This can very quickly become unreadable if the constructor has a lot of dependencies.

func NewHandler(users *UserGateway, comments *CommentGateway, posts *PostGateway, votes *VoteGateway, authz *AuthZGateway) *Handler {
  // ...
}

A pattern employed to improve readability in a situation like this is to create a struct that lists all the parameters of the function as fields and changing the function to accept that struct instead. This is referred to as a parameter object.

Dig has first class support for parameter objects: any struct embedding dig.In gets treated as a parameter object. The following is equivalent to the constructor above.

type HandlerParams struct {
  dig.In

  Users    *UserGateway
  Comments *CommentGateway
  Posts    *PostGateway
  Votes    *VoteGateway
  AuthZ    *AuthZGateway
}

func NewHandler(p HandlerParams) *Handler {
  // ...
}

Handlers can receive any combination of parameter objects and parameters.

func NewHandler(p HandlerParams, l *log.Logger) *Handler {
  // ...
}

Result Objects

Result objects are the flip side of parameter objects. These are structs that represent multiple outputs from a single function as fields in the struct. Structs embedding dig.Out get treated as result objects.

func SetupGateways(conn *sql.DB) (*UserGateway, *CommentGateway, *PostGateway, error) {
  // ...
}

The above is equivalent to,

type Gateways struct {
  dig.Out

  Users    *UserGateway
  Comments *CommentGateway
  Posts    *PostGateway
}

func SetupGateways(conn *sql.DB) (Gateways, error) {
  // ...
}

Optional Dependencies

Constructors often don't have a hard dependency on some types and are able to operate in a degraded state when that dependency is missing. Dig supports declaring dependencies as optional by adding an `optional:"true"` tag to fields of a dig.In struct.

Fields in a dig.In structs that have the `optional:"true"` tag are treated as optional by Dig.

type UserGatewayParams struct {
  dig.In

  Conn  *sql.DB
  Cache *redis.Client `optional:"true"`
}

If an optional field is not available in the container, the constructor will receive a zero value for the field.

func NewUserGateway(p UserGatewayParams, log *log.Logger) (*UserGateway, error) {
  if p.Cache != nil {
    log.Print("Logging disabled")
  }
  // ...
}

Constructors that declare dependencies as optional MUST handle the case of those dependencies being absent.

The optional tag also allows adding new dependencies without breaking existing consumers of the constructor.

Named Values

Some use cases call for multiple values of the same type. Dig allows adding multiple values of the same type to the container with the use of `name:".."` tags on fields of dig.In and dig.Out structs.

A constructor that produces a dig.Out struct can tag any field with `name:".."` to have the corresponding value added to the graph under the specified name.

type ConnectionResult {
  dig.Out

  ReadWrite *sql.DB `name:"rw"`
  ReadOnly  *sql.DB `name:"ro"`
}

func ConnectToDatabase(...) (ConnectionResult, error) {
  // ...
  return ConnectionResult{ReadWrite: rw, ReadOnly:  ro}, nil
}

Another constructor can consume these values by adding fields to a dig.In struct with the same name AND type.

type GatewayParams struct {
  dig.In

  WriteToConn  *sql.DB `name:"rw"`
  ReadFromConn *sql.DB `name:"ro"`
}

The name tag may be combined with the optional tag to declare the dependency optional.

type GatewayParams struct {
  dig.In

  WriteToConn  *sql.DB `name:"rw"`
  ReadFromConn *sql.DB `name:"ro" optional:"true"`
}

func NewCommentGateway(p GatewayParams, log *log.Logger) (*CommentGateway, error) {
  if p.ReadFromConn == nil {
    log.Print("Warning: Using RW connection for reads")
    p.ReadFromConn = p.WriteToConn
  }
  // ...
}
Example (Minimal)
package main

import (
	"encoding/json"
	"log"
	"os"

	"go.uber.org/dig"
)

func main() {
	type Config struct {
		Prefix string
	}

	c := dig.New()

	// Provide a Config object. This can fail to decode.
	err := c.Provide(func() (*Config, error) {
		// In a real program, the configuration will probably be read from a
		// file.
		var cfg Config
		err := json.Unmarshal([]byte(`{"prefix": "[foo] "}`), &cfg)
		return &cfg, err
	})
	if err != nil {
		panic(err)
	}

	// Provide a way to build the logger based on the configuration.
	err = c.Provide(func(cfg *Config) *log.Logger {
		return log.New(os.Stdout, cfg.Prefix, 0)
	})
	if err != nil {
		panic(err)
	}

	// Invoke a function that requires the logger, which in turn builds the
	// Config first.
	err = c.Invoke(func(l *log.Logger) {
		l.Print("You've been invoked")
	})
	if err != nil {
		panic(err)
	}

}
Output:

[foo] You've been invoked

Index

Examples

Constants

View Source
const Version = "1.1.0"

Version of the library

Variables

This section is empty.

Functions

func IsIn added in v1.0.0

func IsIn(o interface{}) bool

IsIn checks whether the given struct is a dig.In struct. A struct qualifies as a dig.In struct if it embeds the dig.In type or if any struct that it embeds is a dig.In struct. The parameter may be the reflect.Type of the struct rather than the struct itself.

A struct MUST qualify as a dig.In struct for its fields to be treated specially by dig.

See the documentation for dig.In for a comprehensive list of supported tags.

func IsOut added in v1.0.0

func IsOut(o interface{}) bool

IsOut checks whether the given struct is a dig.Out struct. A struct qualifies as a dig.Out struct if it embeds the dig.Out type or if any struct that it embeds is a dig.Out struct. The parameter may be the reflect.Type of the struct rather than the struct itself.

A struct MUST qualify as a dig.Out struct for its fields to be treated specially by dig.

See the documentation for dig.Out for a comprehensive list of supported tags.

func RootCause added in v1.1.0

func RootCause(err error) error

RootCause returns the original error that caused the provided dig failure.

RootCause may be used on errors returned by Invoke to get the original error returned by a constructor or invoked function.

Types

type Container

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

Container is a directed acyclic graph of types and their dependencies.

func New

func New(opts ...Option) *Container

New constructs a Container.

func (*Container) Invoke

func (c *Container) Invoke(function interface{}, opts ...InvokeOption) error

Invoke runs the given function after instantiating its dependencies.

Any arguments that the function has are treated as its dependencies. The dependencies are instantiated in an unspecified order along with any dependencies that they might have.

The function may return an error to indicate failure. The error will be returned to the caller as-is.

func (*Container) Provide

func (c *Container) Provide(constructor interface{}, opts ...ProvideOption) error

Provide teaches the container how to build values of one or more types and expresses their dependencies.

The first argument of Provide is a function that accepts zero or more parameters and returns one or more results. The function may optionally return an error to indicate that it failed to build the value. This function will be treated as the constructor for all the types it returns. This function will be called AT MOST ONCE when a type produced by it, or a type that consumes this function's output, is requested via Invoke. If the same types are requested multiple times, the previously produced value will be reused.

In addition to accepting constructors that accept dependencies as separate arguments and produce results as separate return values, Provide also accepts constructors that specify dependencies as dig.In structs and/or specify results as dig.Out structs.

func (*Container) String

func (c *Container) String() string

String representation of the entire Container

type In

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

In may be embedded into structs to request dig to treat them as special parameter structs. When a constructor accepts such a struct, instead of the struct becoming a dependency for that constructor, all its fields become dependencies instead. See the section on Parameter Objects in the package-level documentation for more information.

Fields of the struct may optionally be tagged to customize the behavior of dig. The following tags are supported,

name        Requests a value with the same name and type from the
            container. See Named Values for more information.
optional    If set to true, indicates that the dependency is optional and
            the constructor gracefully handles its absence.

type InvokeOption added in v1.0.0

type InvokeOption interface {
	// contains filtered or unexported methods
}

An InvokeOption modifies the default behavior of Invoke. It's included for future functionality; currently, there are no concrete implementations.

type Option added in v1.0.0

type Option interface {
	// contains filtered or unexported methods
}

Option configures a Container. It's included for future functionality; currently, there are no concrete implementations.

type Out

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

Out may be embedded into structs to request dig to treat them as special result structs. When a constructor returns such a struct, instead of the struct becoming a result of the constructor, all its fields become results of the constructor. See the section on Result Objects in the package-level documentation for more information.

Fields of the struct may optionally be tagged to customize the behavior of dig. The following tags are supported,

name        Specifies the name of the value. Only a field on a dig.In
            struct with the same 'name' annotation can receive this
            value. See Named Values for more information.

type ProvideOption added in v1.0.0

type ProvideOption interface {
	// contains filtered or unexported methods
}

A ProvideOption modifies the default behavior of Provide. It's included for future functionality; currently, there are no concrete implementations.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL