di

package module
v1.12.0 Latest Latest
Warning

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

Go to latest
Published: Jun 8, 2023 License: MIT Imports: 8 Imported by: 25

README

DI

Documentation GitHub release (latest by date) GitHub Workflow Status (with branch) Go Report Card Codecov

DI is a dependency injection library for the Go programming language.

Dependency injection is a form of inversion of control that increases modularity and extensibility in your programs. This library helps you organize responsibilities in your codebase and makes it easy to combine low-level implementations into high-level behavior without boilerplate.

Features

  • Intuitive auto wiring
  • Interface implementations
  • Constructor injection
  • Optional injection
  • Field injection
  • Lazy-loading
  • Tagging
  • Grouping
  • Iteration
  • Decoration
  • Cleanup
  • Container Chaining / Scopes

Installation

go get github.com/defval/di

Documentation

You can use the standard pkg.go.dev and inline code comments. If you are new to auto-wiring libraries such as google/wire or uber-go/dig, start with the tutorial.

Essential Reading

Example Usage

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"

	"github.com/defval/di"
)

func main() {
	di.SetTracer(&di.StdTracer{})
	// create container
	c, err := di.New(
		di.Provide(NewContext),  // provide application context
		di.Provide(NewServer),   // provide http server
		di.Provide(NewServeMux), // provide http serve mux
		// controllers as []Controller group
		di.Provide(NewOrderController, di.As(new(Controller))),
		di.Provide(NewUserController, di.As(new(Controller))),
	)
	// handle container errors
	if err != nil {
		log.Fatal(err)
	}
	// invoke function
	if err := c.Invoke(StartServer); err != nil {
		log.Fatal(err)
	}
}

Full code available here.

Questions

If you have any questions, feel free to create an issue.

Documentation

Overview

Package di provides opinionated way to connect your application components. Container allows you to inject dependencies into constructors or structures without the need to have specified each argument manually.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrTypeNotExists causes when type not found in container.
	ErrTypeNotExists = errors.New("not exists in the container")
)

Functions

func SetTracer

func SetTracer(t Tracer)

SetTracer sets global tracer.

Types

type Constructor

type Constructor interface{}

Constructor is a function with follow signature:

func NewHTTPServer(addr string, handler http.Handler) (server *http.Server, cleanup func(), err error) {
	server := &http.Server{
		Addr: addr,
	}
	cleanup = func() {
		server.Close()
	}
	return server, cleanup, nil
}

This constructor function teaches container how to build server. Arguments (addr and handler) in this function is a dependencies. They will be resolved automatically when someone needs a server. Constructor may have unlimited count of dependencies, but note that container should know how build each of them. Second result of this function is a optional cleanup callback. It describes that container will do on shutdown. Third result is a optional error. Sometimes our types cannot be constructed.

type Container

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

Container is a dependency injection container.

func New

func New(options ...Option) (_ *Container, err error)

New constructs container with provided options. Example usage (simplified):

Define constructors and invocations:

func NewHTTPServer(mux *http.ServeMux) *http.Server {
	return &http.Server{
		Handler: mux,
	}
}

func NewHTTPServeMux() *http.ServeMux {
	return http.ServeMux{}
}

func StartServer(server *http.Server) error {
	return server.ListenAndServe()
}

Use it with container:

container, err := di.New(
	di.Provide(NewHTTPServer),
	di.Provide(NewHTTPServeMux),
	di.Invoke(StartServer),
)
if err != nil {
	// handle error
}

func (*Container) AddParent

func (c *Container) AddParent(parent *Container) error

AddParent adds a parent container. Types are resolved from the container, it's parents, and ancestors. An error is a cycle is detected in ancestry tree.

func (*Container) Apply

func (c *Container) Apply(options ...Option) error

Apply applies options to container.

err := container.Apply(
	di.Provide(NewHTTPServer),
)
if err != nil {
	// handle error
}

func (*Container) Cleanup

func (c *Container) Cleanup()

Cleanup runs destructors in reverse order that was been created.

func (*Container) Has

func (c *Container) Has(target Pointer, options ...ResolveOption) (bool, error)

Has checks that type exists in container, if not it return false.

var server *http.Server
if container.Has(&server) {
	// handle server existence
}

It like Resolve() but doesn't instantiate a type.

func (*Container) Invoke

func (c *Container) Invoke(invocation Invocation, options ...InvokeOption) error

Invoke calls the function fn. It parses function parameters. Looks for it in a container. And invokes function with them. See Invocation for details.

func (*Container) Iterate

func (c *Container) Iterate(target Pointer, fn IterateFunc, options ...ResolveOption) error

Iterate iterates over group of Pointer type with IterateFunc.

 var servers []*http.Server
 iterFn := func(tags di.Tags, loader ValueFunc) error {
		i, err := loader()
		if err != nil {
			return err
		}
		// do stuff with result: i.(*http.Server)
		return nil
 }
 container.Iterate(&servers, iterFn)

func (*Container) Provide

func (c *Container) Provide(constructor Constructor, options ...ProvideOption) error

Provide provides to container reliable way to build type. The constructor will be invoked lazily on-demand. For more information about constructors see Constructor interface. ProvideOption can add additional behavior to the process of type resolving.

func (*Container) ProvideValue

func (c *Container) ProvideValue(value Value, options ...ProvideOption) error

ProvideValue provides value as is.

func (*Container) Resolve

func (c *Container) Resolve(ptr Pointer, options ...ResolveOption) error

Resolve resolves type and fills target pointer.

var server *http.Server
if err := container.Resolve(&server); err != nil {
	// handle error
}

type Decorator

type Decorator func(value Value) error

Decorator can modify container instance.

type Inject

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

Inject indicates that struct public fields will be injected automatically.

type Application struct {
	di.Inject

	Server *http.Server // will be injected
}

You can specify tags for injected types:

 type Application struct {
 	di.Inject

	Public 	*http.Server `type:"public"` 	// *http.Server with type:public tag combination will be injected
	Private *http.Server `type:"private"` 	// *http.Server with type:private tag combination will be injected
 }

type Interface

type Interface interface{}

Interface is a pointer to interface, like new(http.Handler). Tell container that provided type may be used as interface.

type Invocation

type Invocation interface{}

Invocation is a function whose signature looks like:

func StartServer(server *http.Server) error {
	return server.ListenAndServe()
}

Like a constructor invocation may have unlimited count of arguments and they will be resolved automatically. The invocation can return an optional error. Error will be returned as is.

type InvokeOption

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

InvokeOption is a functional option interface that modify invoke behaviour.

type InvokeParams

type InvokeParams struct {
	// The function
	Fn interface{}
}

InvokeParams is a invoke parameters.

type IterateFunc

type IterateFunc func(tags Tags, value ValueFunc) error

IterateFunc function that will be called on each instance in iterate selection.

type Option

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

Option is a functional option that configures container. If you don't know about functional options, see https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis. Below presented all possible options with their description:

  • di.Provide - provide constructors
  • di.ProvideValue - provide value
  • di.Invoke - add invocations
  • di.Resolve - resolves type

func Invoke

func Invoke(fn Invocation, options ...InvokeOption) Option

Invoke returns container option that registers container invocation. All invocations will be called on di.New() after processing di.Provide() options. See Container.Invoke() for details.

func Options

func Options(options ...Option) Option

Options group together container options.

account := di.Options(
  di.Provide(NewAccountController),
  di.Provide(NewAccountRepository),
)
auth := di.Options(
  di.Provide(NewAuthController),
  di.Provide(NewAuthRepository),
)
container, err := di.New(
  account,
  auth,
)
if err != nil {
  // handle error
}

func Provide

func Provide(constructor Constructor, options ...ProvideOption) Option

Provide returns container option that provides to container reliable way to build type. The constructor will be invoked lazily on-demand. For more information about constructors see Constructor interface. ProvideOption can add additional behavior to the process of type resolving.

func ProvideValue

func ProvideValue(value Value, options ...ProvideOption) Option

ProvideValue provides value as is.

func Resolve

func Resolve(target Pointer, options ...ResolveOption) Option

Resolve returns container options that resolves type into target. All resolves will be done on compile stage after call invokes.

type Pointer

type Pointer interface{}

type ProvideOption

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

ProvideOption is a functional option interface that modify provide behaviour. See di.As(), di.WithName().

func As

func As(interfaces ...Interface) ProvideOption

As returns provide option that specifies interfaces for constructor resultant type.

INTERFACE USAGE:

You can provide type as interface and resolve it later without using of direct implementation. This creates less cohesion of code and promotes be more testable.

Create type constructors:

func NewServeMux() *http.ServeMux {
	return &http.ServeMux{}
}

func NewServer(handler *http.Handler) *http.Server {
	return &http.Server{
		Handler: handler,
	}
}

Build container with di.As provide option:

container, err := di.New(
	di.Provide(NewServer),
	di.Provide(NewServeMux, di.As(new(http.Handler)),
)
if err != nil {
	// handle error
}
var server *http.Server
if err := container.Resolve(&http.Server); err != nil {
	// handle error
}

In this example you can see how container inject type *http.ServeMux as http.Handler interface into the server constructor.

GROUP USAGE:

Container automatically creates group for interfaces. For example, you can use type []http.Handler in previous example.

var handlers []http.Handler
if err := container.Resolve(&handlers); err != nil {
	// handle error
}

Container checks that provided type implements interface if not cause compile error.

func Decorate

func Decorate(decorators ...Decorator) ProvideOption

Decorate will be called after type construction. You can modify your pointer types.

func WithName

func WithName(name string) ProvideOption

WithName modifies Provide() behavior. It adds name identity for provided type. Deprecated: use di.Tags.

type ProvideParams

type ProvideParams struct {
	Tags       Tags
	Interfaces []Interface
	Decorators []Decorator
}

ProvideParams is a Provide() method options. Name is a unique identifier of type instance. Provider is a constructor function. Interfaces is a interface that implements a provider result type.

type ResolveOption

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

ResolveOption is a functional option interface that modify resolve behaviour.

func Name

func Name(name string) ResolveOption

Name specifies provider string identity. It needed when you have more than one definition of same type. You can identity type by name. Deprecated: use di.Tags

type ResolveParams

type ResolveParams struct {
	Tags Tags
}

ResolveParams is a resolve parameters.

type StdTracer

type StdTracer struct {
}

StdTracer traces dependency injection cycle to stdout.

func (StdTracer) Trace

func (s StdTracer) Trace(format string, args ...interface{})

Trace traces debug information with default logger.

type Tags

type Tags map[string]string

Tags is a string representation of key value pairs.

type Server struct {
	di.Tags `http:"true" server:"true"`
}
_, err := di.New(
	di.Provide(func() *Server { return &Server{} }),
)
var s *Server
c.Resolve(&s, di.Tags{"http": "true", "server": "true"})

func (Tags) String

func (t Tags) String() string

String is a tags string representation.

type Tracer

type Tracer interface {
	// Trace prints library logs.
	Trace(format string, args ...interface{})
}

Tracer traces dependency injection cycle.

type Value

type Value interface{}

Value is a variable of provided or resolved type.

type ValueFunc

type ValueFunc func() (interface{}, error)

ValueFunc is a lazy-loading wrapper for iteration.

Directories

Path Synopsis
_examples

Jump to

Keyboard shortcuts

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