GoDoc
Go Report Card

Dependency injection

DI is a dependency injection library that is focused on clean API and flexibility. DI has two types of top-level abstractions:
Container and Resolver. First one is responsible for accepting constructors and instances and creating abstraction bindings
out of them. Second implements different instance resolution scenarios against one or more Containers.

Initially this library was heavily inspired by GoLobby Container but since then
had a lot of backwards incompatible changes in structure, functionality and API.
To install DI simply run in your project directory:

go get github.com/HnH/di

Container

type Container interface {
    Singleton(constructor interface{}, opts ...Option) error
    Instance(instance interface{}, name string) error
    Factory(constructor interface{}, opts ...Option) error
    ListBindings(reflect.Type) (map[string]Binding, error)
    Reset()
}

Singleton

Singleton() method requires a constructor which will return Implementation(s) of Abstraction(s). Constructor will be called once
and returned Implementations(s) will later always bound to Abstraction(s) on resolution requests.

err = di.Singleton(func() (Abstraction, SecondAbstraction) {
    return Implementation, SecondImplementation
})

// Singleton may also accept naming option which means that returned Implementation will be available only under provided name
err = di.Singleton(func() (Abstraction) {
    return Implementation
}, di.WithName("customName"))

// Name can be provided for each of the Implementations if there are more than one
err = di.Singleton(func() (Abstraction, SecondAbstraction) {
    return Implementation, SecondImplementation
}, di.WithName("customName", "secondCustomName"))

// If there is only one Implementation returned you may give multiple aliases for it.
err = di.Singleton(func() (Abstraction) {
    return Implementation
}, di.WithName("customName", "secondCustomName"))

Factory

Factory() method requires a constructor which will return exactly one Implementation of exactly one Abstraction.
Constructor will be called on each Abstraction resolution request.

err = di.Factory(func() (Abstraction) {
    return Implementation
})

// Factory also optionally accepts naming option which means that returned Implementation will be available only under provided name
err := di.Factory(func() (Abstraction) {
    return Implementation
}, di.WithName("customName"))

Resolver

type Resolver interface {
    With(instances ...interface{}) Resolver
    Resolve(receiver interface{}, opts ...Option) error
    Call(function interface{}, opts ...Option) error
    Fill(receiver interface{}) error
}

Resolve

Resolve() requires a receiver (pointer) of an Abstraction and fills it with appropriate Implementation.

var abs Abstraction
err = di.Resolve(&a)

// Resolution can be done with previously registered names as well
err = di.Resolve(&a, di.WithName("customName"))

Call

The Call() executes as function with resolved Implementation as a arguments.

err = di.Call(func(a Abstraction) {
    // `a` will be an implementation of the Abstraction
})

// returned values can be bound to variables by providing an option
var db Database
err = di.Call(func(a Abstraction) Database {
    return &MySQL{a}
}, di.WithReturn(&db))
// db == &MySQL{a}

Fill

The Fill() method takes a struct (pointer) and resolves its fields. The example below expresses how the Fill() method works.

err = di.Singleton(func() Mailer { return &someMailer{} })

err = di.Singleton(func() (Database, Database) {
    return &MySQL{}, &Redis{} 
}, di.WithName("data", "cache"))

type App struct {
    mailer Mailer    `di:"type"`
    data   Database  `di:"name"`
    cache  Database  `di:"name"`
    x int
}

myApp := App{}

err := container.Fill(&myApp)

// [Typed Bindings]
// `myApp.mailer` will be an implementation of the Mailer interface

// [Named Bindings]
// `myApp.data` will be a MySQL implementation of the Database interface
// `myApp.cache` will be a Redis implementation of the Database interface

// `myApp.x` will be ignored since it has no `di` tag

Alternatively map[string]Type or []Type can be provided. It will be filled with all available implementations of provided Type.

var list []Shape
container.Fill(&list)

// []Shape{&Rectangle{}, &Circle{}}

var list map[string]Shape
container.Fill(&list)

// map[string]Shape{"square": &Rectangle{}, "rounded": &Circle{}} 

GitHub

View Github