/graph

Dependency and Lifecycle Management for Go

Primary LanguageGoApache License 2.0Apache-2.0

Graph: Dependency and Lifecycle Management

CircleCI

Dependency injection is a technique to "magically" satisfy dependencies within an application, reducing the mental overhead for programmer and ensuring the dependencies remain loosely coupled.

This module for Go provides one implementation of such magic, with the following aims:

  • Provide dependency injection for struct fields;
  • Explicit lifecycle management for an application;
  • Mapping between an interface and its' concrete implementation through module imports;
  • Passing of state between dependencies in different phases of the lifecycle;
  • A framework for developing tools and unit tests.

The Graph module provides a programming pattern which aims to target the best features of Go (channels, goroutines and composition for example) to simplify complex application development.

Installing & Using

To use Graph just import the definitions to create a Unit, an instance which can be used in dependency injection. Mark any Unit with an anonymous graph.Unit field. For example, mymodule.MyInterface can be injected into an application shell tool:

package main

import (
  graph "github.com/djthorpe/graph"
  tool "github.com/djthorpe/graph/pkg/tool"
  "mymodule"
)

type App struct {
  graph.Unit
  mymodule.MyInterface
}

func main() {
  tool.Shelltool(context.WithCancel(/* ... */),"myapp",os.Args[1:],new(App))
}

The magic here is that the MyInterface dependency is satisifed without further code, and according to the lifecycle can resolve its' own dependencies and even run background tasks. When it terminates it can also dispose of any used resources.

Somewhat less magically, here is the implementation of the interface, and definition of the lifecycle:

package mymodule

import (
  graph "github.com/djthorpe/graph"
  "reflect"
)

func init() {
  // Register mymodule.myUnit as implementation of exported mymodule.MyInterface
  graph.RegisterUnit(
      reflect.TypeOf(&myUnit{}), 
      reflect.TypeOf((*MyInterface)(nil))
  )
}

type MyInterface interface {
  // ... interface definition
}

type myUnit struct {
  graph.Unit
  graph.Events // Inject event pubsub dependency
  // ... other dependencies injected here
}

// New, Run and Dispose define the lifecycle
func (*myUnit) New(graph.State) error {
  // ...Initialize myUnit
}

func (*myUnit) Run(context.Context) error {
  // ...Run myUnit until cancel or deadline exceeded
}

func (*myUnit) Dispose() error {
  // ...Dispose of any resources used by myUnit
}

More information about the lifecycle, passing state between units, testing and so forth is provided in the documentation.

Documentation

More information on usage of Graph is provided in the following documentation:

Project Status

This module is currently in development but is mostly feature-complete.

Community

  • File an issue or question on github.
  • Licensed under Apache 2.0, please read that license about using and forking Graph. The main conditions require preservation of copyright and license notices. Contributors provide an express grant of patent rights. Licensed works, modifications, and larger works may be distributed under different terms and without source code.