// Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // 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 Named // Values. // // Named Values can be produced by passing the dig.Name option when a // constructor is provided. All values produced by that constructor will have // the given name. // // Given the following constructors, // // func NewReadOnlyConnection(...) (*sql.DB, error) // func NewReadWriteConnection(...) (*sql.DB, error) // // You can provide *sql.DB into a Container under different names by passing // the dig.Name option. // // c.Provide(NewReadOnlyConnection, dig.Name("ro")) // c.Provide(NewReadWriteConnection, dig.Name("rw")) // // Alternatively, you can produce a dig.Out struct and tag its fields with // `name:".."` to have the corresponding value added to the graph under the // specified name. // // type ConnectionResult struct { // dig.Out // // ReadWrite *sql.DB `name:"rw"` // ReadOnly *sql.DB `name:"ro"` // } // // func ConnectToDatabase(...) (ConnectionResult, error) { // // ... // return ConnectionResult{ReadWrite: rw, ReadOnly: ro}, nil // } // // Regardless of how a Named Value was produced, it can be consumed by another // constructor by accepting a dig.In struct which has exported fields with the // same name AND type that you provided. // // 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 // } // // ... // } // // Value Groups // // Added in Dig 1.2. // // Dig provides value groups to allow producing and consuming many values of // the same type. Value groups allow constructors to send values to a named, // unordered collection in the container. Other constructors can request all // values in this collection as a slice. // // Constructors can send values into value groups by returning a dig.Out // struct tagged with `group:".."`. // // type HandlerResult struct { // dig.Out // // Handler Handler `group:"server"` // } // // func NewHelloHandler() HandlerResult { // .. // } // // func NewEchoHandler() HandlerResult { // .. // } // // Any number of constructors may provide values to this named collection. // Other constructors can request all values for this collection by requesting // a slice tagged with `group:".."`. This will execute all constructors that // provide a value to that group in an unspecified order. // // type ServerParams struct { // dig.In // // Handlers []Handler `group:"server"` // } // // func NewServer(p ServerParams) *Server { // server := newServer() // for _, h := range p.Handlers { // server.Register(h) // } // return server // } // // Note that values in a value group are unordered. Dig makes no guarantees // about the order in which these values will be produced. // // Value groups can be used to provide multiple values for a group from a // dig.Out using slices, however considering groups are retrieved by requesting // a slice this implies that the values must be retrieved using a slice of // slices. As of dig v1.9.0, if you want to provide individual elements to the // group instead of the slice itself, you can add the `flatten` modifier to the // group from a dig.Out. // // type IntResult struct { // dig.Out // // Handler []int `group:"server"` // [][]int from dig.In // Handler []int `group:"server,flatten"` // []int from dig.In // } // package dig // import "go.uber.org/dig"