Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
6 contributors

Users who have contributed to this file

@abhinav @glibsm @anuptalwalkar @sagikazarmark @breerly @akshayjshah
335 lines (333 sloc) 10.2 KB
// 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.
package dig // import "go.uber.org/dig"
You can’t perform that action at this time.