godim

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2020 License: MIT Imports: 8 Imported by: 0

README

Build Status codecov Go Report Card

godim - Go Dependency injection management

Version

v0.5 - Alpha Version - Everything may change

Features

  • Lifecycle management
  • Simple Tag Declaration and injection
  • Configuration injection
  • Event exchange throu the application

Usage

Godim use tags to inject configuration and dependencies in a struct.

Default Usage

A little example is sometimes better than a full explanation

package main

import (
  "github.com/ekino/godim"
  "fmt"
)

type MyHandler struct {
  UserService *UserService `inject:"UserService"`
}

func (mh *MyHandler) doIt() {
  mh.UserService.doIt()
}

type UserService struct {
  MyKey string `config:"user.key"`
}

func (us *UserService) doIt() {
  fmt.Println("done :", us.MyKey)
}

func config(key string, kind reflect.Kind) (interface{}, error) {
  return "myuserKey"
}

func main(){
  mh := MyHandler{}
  us := UserService{}
  g := godim.NewGodim(godim.DefaultConfig().WithConfigurationFunction())
  g.DeclareDefault(&mh,&us)
  g.RunApp()
  
  mh.doIt()
  
}

will print

done :myuserKey
Advanced usage
Name it

Implementing Identifier interface allows godim to know how you want to name your struct

type Identifier interface {
	Key() string
}

By default, naming convention will use class definition. For instance :

package main

type UserService struct {

}

will have a name : main.UserService

Profile

You can define policies on how you want to enforce linking of your different layer. For instance StrictHTTPProfile will define 3 kinds of layers:

  • handler
  • service
  • repository

with a strict linking between them : repository can be injected in service, service can be injected in handler, all others possibilities are prohibited

AutoConfiguration

Providing to Godim a function like this one

func configFunc (key string, value reflect.Value) (interface{}, error) {
...
}

will allow configuration parameters to be injected directly in your structs throu config tag see Godim-Viper for an implementation of this function with Viper.

Specific initialization or closing

It is sometimes useful to initialize some things like connection to db during the life of the your app Two interfaces can be implemented for struct that needs specific

type Initializer interface {
	OnInit() error
}
type Closer interface {
	OnClose() error
}

OnInit will be called after configuration and injection phases. OnClose will be called when you close your app

godim.CloseApp() 
Lifecycle order

The current lifecycle order of godim will go through

  • Declaration phase. use godim.Declare(...)
  • Configuration phase, take all your config tags and fill them
  • Injection phase, take all your injection tags and link them
  • Initialization phase, call all OnInit() func declared
  • Running phase, your turn
  • Closing phase, call all OnClose() func declared
Initialization priorization

You can handle the OnInit order if you need to, by implementing the following interface in your struct:

type Prioritizer interface {
	Priority() int
}

Default priority is set to 0, by implementing this function you can say if you want to execute the OnInit method sooner (by returning a lower value) or later (with a higher value).

Event Switch

Godim comes with a simple event switch that enable Event to be emitted from anywhere and received everywhere. The definition of events Type throu the application is static, there is no dynamic declaration during the run lifecycle.

An Event looks like

type Event struct {
  Type string
  Payload map[string]interface{}
}

You need to consider events as immutable object (Read Only), as they can be accessed concurrently. But there is no protection against write in go yet

To emit an event your struct can implement EventEmitter and call Emit to send the event

type MyStruct struct {
  EventEmitter
}
...
myStruct := &MyStruct{}
myStruct.Emit(&Event{Type:"a"})

There is 3 ways to observe those events, depending on the phase you want to interact with it. After being emitted the event is buffered in the event switch. For each event, a go routine is launched, orchestrate a first level of interaction with EventInterceptor, interceptor have a priority, the lowest the first. During this phase an event can be aborted : it will stop propagation of the event on higher priority of interceptor or receiver. An aborted event will still go throu EventFinalizer if one is declared. After orchestration phase, events are transmitted to each receiver in his own go routine in a choregraphic way. When all events are managed by all interceptor and receivers, event finish his course in an EventFinalizer.

To intercept an event you need to implements EventInterceptor in your struct

type EventInterceptor interface {
	Identifier
	Intercept(*Event) error
	InterceptPriority() int
}

To subscribe to an event you need to implements EventReceiver interface in your struct

type EventReceiver interface {
  Identifier
	ReceiveEvent(*Event)
	HandleEventTypes() []string
}

the HandleEventTypes method defines the subscribe events type by this receiver. the ReceiveEvent will receive all events of type declared in the previous method.

EventFinalizer does not interact with event metadata. Typical usage is a final save in a db of the event. EventFinalizer interface follow :

type EventFinalizer interface {
	Finalize(*Event)
}

There can be only one event finalizer in your application.

Documentation

Index

Constants

View Source
const Version = "0.5.0-alpha"

Version is the current godim version

Variables

View Source
var DefaultProfile = Profile{
	// contains filtered or unexported fields
}

DefaultProfile specific default profile that allows anything to be injected into anything

Functions

This section is empty.

Types

type AppProfile

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

AppProfile represents the differents Profile of your application see @Profile for Profile detail

func HTTPAppProfile

func HTTPAppProfile() *AppProfile

HTTPAppProfile return a less strict http profile based on :

- handler : manage http input and can call services

- service : manage business logic and can call repositories and other services

- repository : manage data access

func StrictHTTPAppProfile

func StrictHTTPAppProfile() *AppProfile

StrictHTTPAppProfile return a strict http profile based on :

- handler : manage http input and can call service

- service : manage business logic and can call repositories

- repository : manage data access

- driver : manage resource driver

func (*AppProfile) AddProfile

func (ap *AppProfile) AddProfile(p *Profile) error

AddProfile add a profile

func (*AppProfile) AddProfileDef

func (ap *AppProfile) AddProfileDef(name string, injectIn ...string) error

AddProfileDef add a new profile definition

- name name to use in injection

- injectIn where it can be injected

type Closer

type Closer interface {
	OnClose() error
}

Closer interface to implement if you need specific closing method

OnClose will be called on close phase

type Config

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

Config struct for Godim

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig declare a default configuration

func NewConfig

func NewConfig() *Config

NewConfig declare a new config

func (*Config) Build

func (c *Config) Build() *Godim

Build lock profile and build godim

func (*Config) WithAppProfile

func (c *Config) WithAppProfile(ap *AppProfile) *Config

WithAppProfile declare the app profile to use

func (*Config) WithConfigString

func (c *Config) WithConfigString(config string) *Config

WithConfigString use a new config string

func (*Config) WithConfigurationFunction

func (c *Config) WithConfigurationFunction(f func(key string, val reflect.Value) (interface{}, error)) *Config

WithConfigurationFunction declare your configuration function

func (*Config) WithEventSwitch

func (c *Config) WithEventSwitch(bufferSize int) *Config

WithEventSwitch start an event switch with godim

func (*Config) WithInjectString

func (c *Config) WithInjectString(inject string) *Config

WithInjectString use a new inject string

type Counter

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

type Emitter

type Emitter interface {
	Emit(*Event)
	// contains filtered or unexported methods
}

type ErrType

type ErrType uint64

ErrType base type for Error Type

const (
	// ErrTypeInjection happens in injection phase failure
	ErrTypeInjection ErrType = 1 << 63
	// ErrTypeProfile happens in profile definition failure
	ErrTypeProfile ErrType = 1 << 62
	// ErrTypeRegistry happens in internal registry failure
	ErrTypeRegistry ErrType = 1 << 61
	// ErrTypeGodim happens in internal godim failure
	ErrTypeGodim ErrType = 1 << 60
	// ErrTypeEvent happens in internal event switch
	ErrTypeEvent ErrType = 1 << 59
	// ErrTypeAny for any other kind of errors
	ErrTypeAny ErrType = 1 << 1
)

type Error

type Error struct {
	Err  error
	Type ErrType
}

Error main godim error struct

func (Error) Error

func (err Error) Error() string

Error from error interface.

func (*Error) IsErrType

func (err *Error) IsErrType(er ErrType) bool

IsErrType check kind of error.

func (*Error) SetErrType

func (err *Error) SetErrType(er ErrType) *Error

SetErrType sets the error's type.

type Event

type Event struct {
	Type    string
	Payload map[string]interface{}
	// contains filtered or unexported fields
}

Event an Event can be transmitted from an Emitter to any Receiver. It's not yet immutable but needs to be considered as they can be concurrently accessed. The id is set internally and can be overridden only if another generator is used. The metadata can be set with AddMetadata(key string, value interface{}) and can be retrieved with GetMetadata(key string). An event is locked after "interceptor" phase which means its metadata cannot be modified through previous methods.

func (*Event) Abort

func (event *Event) Abort(current EventInterceptor, reason string)

Abort stop the processing of the event. This method could be called from any interceptor. A locked event cannot be aborted.

func (*Event) AddMetadata

func (event *Event) AddMetadata(key string, value interface{}) error

AddMetadata add a key value pair of metadata to the event

An error will occur if the event is locked or if the key exists already.

func (*Event) GetID

func (event *Event) GetID() uint64

func (*Event) GetMetadata

func (event *Event) GetMetadata(key string) interface{}

func (*Event) GetState

func (event *Event) GetState() EventState

type EventEmitter

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

func (*EventEmitter) Emit

func (ee *EventEmitter) Emit(event *Event)

Emit adds metadata to the event then emit it through the channel

type EventFinalizer

type EventFinalizer interface {
	Finalize(*Event)
}

type EventInterceptor

type EventInterceptor interface {
	Identifier
	Intercept(*Event) error
	InterceptPriority() int
}

EventInterceptor any events handled by the EventSwitch goes through the interceptors. An interceptor can abort an Event to stop its processing beyond the interceptor itself. An interceptor must define a priority and 2 interceptors cannot have the same priority.

type EventReceiver

type EventReceiver interface {
	Identifier
	ReceiveEvent(*Event) error
	HandleEventTypes() []string
}

EventReceiver All events are treated in a choreographic pattern, asynchronously. The HandleEventTypes should return the types of event the receiver wants to handle.

type EventState

type EventState int
const (
	// ESEmitted the event is being processed
	ESEmitted EventState = iota
	// ESResolved the event has been successfully processed by the interceptors and the receivers
	ESResolved
	// ESAborted the event processing is stopped which means that it won't be transmitted to any receiver
	ESAborted
	// ESError the event encountered an error during its processing
	ESError
)

type EventSwitch

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

EventSwitch this is not a hub, we want a switch

func NewEventSwitch

func NewEventSwitch(bufferSize int) *EventSwitch

NewEventSwitch build a new event switch

func (*EventSwitch) AddEmitter

func (es *EventSwitch) AddEmitter(e Emitter) error

AddEmitter add an emitter

func (*EventSwitch) AddInterceptor

func (es *EventSwitch) AddInterceptor(e EventInterceptor) error

AddInterceptor add an interceptor. An error is returned when an interceptor with the same priority is already declared.

func (*EventSwitch) AddReceiver

func (es *EventSwitch) AddReceiver(e EventReceiver) error

AddReceiver add a receiver

func (*EventSwitch) Close

func (es *EventSwitch) Close()

Close stop the EventEmitter then closes it channels

func (*EventSwitch) CloseGracefully

func (es *EventSwitch) CloseGracefully()

CloseGracefully wait for all events to be processed then close the EventSwitch This method is blocking until the EventSwitch is stopped.

func (*EventSwitch) Start

func (es *EventSwitch) Start()

Start initialize the EventSwitch and start it

func (*EventSwitch) Stop

func (es *EventSwitch) Stop()

func (*EventSwitch) WithEventFinalizer

func (es *EventSwitch) WithEventFinalizer(f EventFinalizer) *EventSwitch

WithEventFinalizer declare an event finalizer that will be called at the end of an event management

func (*EventSwitch) WithIDGenerator

func (es *EventSwitch) WithIDGenerator(idg IDGenerator) *EventSwitch

WithIDGenerator change the id generator used.

if the switch is running, do nothing

func (*EventSwitch) WithSeed

func (es *EventSwitch) WithSeed(seed uint64) *EventSwitch

WithSeed change the seed of the generator

if the switch is running, do nothing

type Godim

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

Godim is the main app controller

func Default

func Default() *Godim

Default build a default Godim from default configuration

func NewGodim

func NewGodim(config *Config) *Godim

NewGodim build a Godim with specified Config

func (*Godim) CloseApp

func (godim *Godim) CloseApp() error

CloseApp close all things declared in your app

func (*Godim) CloseAppGracefully

func (godim *Godim) CloseAppGracefully() error

CloseApp close all things declared in your app

func (*Godim) Declare

func (godim *Godim) Declare(label string, o ...interface{}) error

Declare specific level

func (*Godim) DeclareDefault

func (godim *Godim) DeclareDefault(o ...interface{}) error

DeclareDefault : declare all your defaults services

func (*Godim) GetStruct

func (godim *Godim) GetStruct(label, key string) interface{}

GetStruct return the stored struct in case it is needed for other usage

func (*Godim) RunApp

func (godim *Godim) RunApp() error

RunApp : Run the application after configuration and injection phase

type IDGenerator

type IDGenerator interface {
	SetSeed(uint64) error
	NextID() uint64
}

type Identifier

type Identifier interface {
	Key() string
}

Identifier interface to implement if you want to name your service.

Key is the key name that will reference it in the other service

type Initializer

type Initializer interface {
	OnInit() error
}

Initializer interface to implement if you need specific initialization

OnInit will be called after Injection phase

type Prioritizer

type Prioritizer interface {
	Priority() int
}

Prioritizer interface to implement if you want to change the initialization order of your service.

Priority is the score that will determine when this service will be instantiated comparing to the others. Default priority is 0. Lower is sooner.

type Profile

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

Profile a Profile represent an injectable type

It holds the possible dependency tree in order to validate non-cycling app

injection example :

type UserService struct {
    userRepository *UserRepository inject:"repository:UserRepository"
}

type Registry

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

Registry the internal registry

type TagConfig

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

TagConfig internal configuration tag

Jump to

Keyboard shortcuts

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