349 lines
10 KiB
Go
349 lines
10 KiB
Go
// 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 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("Caching 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"
|