Help us shape the future of Go! Take the 2026 Go Developer Survey

parsley

module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2024 License: AGPL-3.0

README

parsley

This dependency injection package may become your app's favorite ingredient, like parsley, a herb that fits well in various dishes across different cuisines globally. The parsley library is inspired by other dependency injection libraries I have used, which I always miss when working on Go projects.

Usage

$ go get github.com/matzefriedrich/parsley

Dependency mapping configuration

The dependency mapping configuration requires types, interfaces, and constructor methods—basically, the same things you need to wire dependencies manually.

In parsley, constructor methods are the centerpiece that defines the mappings between abstractions (interfaces) and implementation types; required services are expressed as function parameters. The return type of a constructor method specifies the abstraction, whereby the method itself is responsible for creating the actual object instance, thus acting as the glue between implementation- and interface types.

Learn Parsley by example

Parsley uses reflection to build a service registry and resolve objects at runtime. Use the NewServiceRegistry method to create a registry service that tracks service mapping and lifetime configuration. Use the Register method to register types via constructor methods.

registry := NewServiceRegistry()

_ = RegisterSingleton(sut, NewFoo)
_ = RegisterScoped(sut, NewFooConsumer)

Once all service types are registered, a resolver service (the actual container) must be created using the registry's BuildResolver method. The resolver is responsible for creating object instances and managing their lifetime.

resolver := registry.BuildResolver()
scope := internal.NewScopedContext(context.Background())
consumerInstance, _ := resolver.Resolve(scope, 
    types.ServiceType[FooConsumer]())

The types and methods involved in the example above, are defined as follows:

type Foo interface {
    Bar()
}

type FooConsumer interface {
    FooBar()
}

Next, implementations for those services are defined as follows:

type foo struct{}

func (f *foo) Bar() {}

type fooConsumer struct {
    foo Foo
}

func (c *fooConsumer) FooBar() {
    c.foo.Bar()
}

The Foo service does not have any dependencies. Thus, the NewFoo constructor function has no parameters. It returns a new instance of foo; the resolver is not interested in the actual implementation type of Foo.

In contrast, the constructor function of FooConsumer requires a Foo object. Parsley builds a dependency graph for requested services, resolves those services first (respecting configured lifetimes), and then calls constructor methods with all required parameters.

func NewFoo() Foo {
    return &foo{}
}

func NewFooConsumer(foo Foo) FooConsumer {
    return &fooConsumer{
        foo: foo,
    }
}

Directories

Path Synopsis
pkg

Jump to

Keyboard shortcuts

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