joernlenoch / go-dieb

Easy hierarchical dependency injector based on go's reflection system and influenced by common dependency injection systems.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Dieb (former 'rocket')

Simple hierarchical dependency injector based on go's reflection system and influenced by common dependency injection handlers.

Focuses on a clear and reusable structure, without pitfalls.

Personal Note

Currently in production in 3 of my larger projects (35k loc+).

Works very reliable and we never had any issues.

-- len

Roadmap

  • indirect dependancies via interface

  • direct dependancies via pointer

  • preparation of structs

  • constructor methods

  • 100% test coverage

  • Travis-CI

Usage

Reminder: Services can depend on other services and references!

Create a new service definition. This way, it can be replaced during testing.

NamingService interface {
  Names() []string
}

Create the default implementation for this service.

StaticNamingService struct {}

func (s *StaticNamingService) Names() []string {
  return []{"Carl", "Michael", "Susanne"}
}

Create a new injector and provide instances of the service implementations.

injector := dieb.NewInjector()
defer injector.Shutdown()

injector.Provide(
  &ConfigurationService{},
  &StaticNamingService{},
)

Use the injector to fulfill dependencies.

type NamesController struct{
  NamingService       NamingService `dieb:""` // This is an indirect dependancy
  StaticNamingService *StaticNamingService `dieb:""` // This is a direct dependancy
}

var ctrl NamesController
if err := injector.Prepare(&ctrl); err != nil {
  panic(err)
}

log.Print(ctrl.NamingService.Names())

Init / Constructor methods

Since Version v2.1.0 it is possible to inject via method.

func ConstructorMethod(/* deps */) error {
  
  // [...]
  // Do something with the deps
  // [...]
  
  return nil
}

err := inj.PrepareFunc(ConstructorMethod)
if err != nil {
  panic(err)
}

The Init(..) error method will automatically be called when a service is provided!

Hierarchical injection / Overwrite injected services

Adding a new service with existing interfaces overwrites the previously existing one. Consequently, a previously used service can be injected in the newest one to be used.

BetterNamingService struct {
    Previous NamingService `dieb:""`
}

func (s *BetterNamingService) Names() []string {
	return append(s.Previous.Names(), "Vivian", "Marcus", "Nani"}
}

Will return ["Carl", "Michael", "Susanne", "Vivian", "Marcus", "Nani"] when used like this.

injector := dieb.NewInjector()
defer injector.Shutdown()

injector.Provide(&StaticNamingService{})

// ... do something else ...

injector.Provide(&BetterNamingSystem{})

Use-Cases

A typical example could be a StorageService and a CachingService that provides a storage interface, but applies a custom caching strategy.

Optional dependencies

When declaring dependencies with the dieb annotation, the option dieb:",optional" can be used to make the injector ignore the dependency if it can not be resolved.

Example

type SomeCriticalController struct{
  logger Logger `dieb:",optional"`
}

Lifecycle event handling

Sometimes, it may be useful to construct or deconstruct services before and after usage. To bring this into the overall workflow

type DatabaseService struct{
  mysql *sqlx.DB
}

// dieb.Initer
func (d *DatabaseService) Init() error {
  // ... connect to the database
  return nil
}

// custom Init with dependancies
func (d *DatabaseService) Init(injector dieb.Injector, db *SomeService) error {
  // ... connect to the database
  return nil
}

// dieb.Shutdowner
func (d *DatabaseService) Shutdown() {
  // ... close all open connections ...
}

To work, defer injector.Shutdown() is required.

Usage during testing

[WIP]

Third-Party dependencies

None

Last Update

02.12 - @joernlenoch

About

Easy hierarchical dependency injector based on go's reflection system and influenced by common dependency injection systems.

License:MIT License


Languages

Language:Go 100.0%