mud

package
v0.0.0-...-5c8fa84 Latest Latest
Warning

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

Go to latest
Published: Apr 25, 2024 License: AGPL-3.0 Imports: 12 Imported by: 0

README

mud

mud is a package for lazily starting up a large set of services and chores. The mud package name comes from a common architectural pattern called "Big Ball of Mud".

You can think about mud as a very huge map of service instances: map[type]component. Where component includes the singleton instance and functions to initialize the singleton and run/close them.

Components can also depend on each other, and there are helper function to filter the required components (and/or initialize/start them).

Compared to other similar libraries, like https://github.com/uber-go/fx or https://github.com/uber-go/dig, mud is just a very flexible framework, it wouldn't like to restrict the usage. Therefore advanced workflows also can be implemented with filtering different graphs and using them.

Users of this library has more power (and more responsibility).

Getting started

You can create the instance registry with:

mud := mud.NewBall()

Register a new component:

Provide[your.Service](ball, func() your.Service {
    return your.NewService()
})

Now, your component is registered, but not yet initialized. You should select some of the services to Init / run them:

err := mud.ForEach(ball, mud.Initialize(context.TODO()))
if err != nil {
   panic(err)
}

Now your component is initialized:

fmt.Println(mud.Find(ball, mud.All)[0].Instance())

This one selected the first component (we registered only one), but you can also use different selectors. This one selects the components by type.

fmt.Println(mud.Find(ball, mud.Select[your.Service](ball))[0].Instance())

Or, of you are sure, it's there:

fmt.Println(mud.MustLookup[your.Service](ball))

Dependencies and dependency injection

Dependencies are automatically injected. Let's say you have two structs:

type Service struct {
}

func NewService() Service {
	return Service{}
}

type Endpoint struct {
	Service Service
}

func NewEndpoint(service Service) Endpoint {
	return Endpoint{
		Service: service,
	}
}

Now you can register both:

mud.Provide[your.Service](ball, your.NewService)
mud.Provide[your.Endpoint](ball, your.NewEndpoint)

When you initialize the Endpoint, Service will be injected (if the instance is available!!!):

err := mud.MustDo[your.Service](ball, mud.Initialize(context.TODO()))
if err != nil {
    panic(err)
}

err = mud.MustDo[your.Endpoint](ball, mud.Initialize(context.TODO()))
if err != nil {
    panic(err)
}

But instead of initializing manually, you can also just ask what you need, and initialize everything in the right order

err := mud.ForEachDependency(ball, mud.Select[your.Endpoint](ball), mud.Initialize(context.TODO()), mud.All)

Views

Views are useful when you already have sg. registered, but you would like to make it fully or partially available under different type:

mud.Provide[satellite.DB](ball, OpenSatelliteDB)
mud.View[satellite.DB, gracefulexit.DB](ball, satellite.DB.GracefulExit)

This registers a satellite.DB (first line) and a gracefulexit.DB (second line). And if gracefulexit.DB is needed for injection, it will call the function to get it.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func All

func All(_ *Component) bool

All is a ComponentSelector which matches all components.

func CloseAll

func CloseAll(ball *Ball, timeout time.Duration) error

CloseAll calls the close callback stage on all initialized components.

func DependsOn

func DependsOn[BASE any, DEPENDENCY any](ball *Ball)

DependsOn creates a dependency relation between two components. With the help of the dependency graph, they can be executed in the right order.

func Dereference

func Dereference[A any](a *A) A

Dereference is a simple transformation to make real value from a pointer. Useful with View. for example: `View[*DB, DB](ball, Dereference[DB])`.

func Dot

func Dot(w io.Writer, components []*Component) (err error)

Dot generates graph report of the modules in dot format, but only the selected components are included.

func DotAll

func DotAll(w io.Writer, ball *Ball) (err error)

DotAll generates graph report of the modules in dot format.

func Execute

func Execute[A any](ctx context.Context, ball *Ball, factory interface{}) (A, error)

Execute executes a function with injecting all the required dependencies with type based Dependency Injection.

func Execute0

func Execute0(ctx context.Context, ball *Ball, factory interface{}) error

Execute0 executes a function with injection all required parameters. Same as Execute but without return type.

func ForEach

func ForEach(ball *Ball, callback func(component *Component) error, selectors ...ComponentSelector) error

ForEach executes a callback action on all the selected components.

func ForEachDependency

func ForEachDependency(ball *Ball, target ComponentSelector, callback func(component *Component) error, selectors ...ComponentSelector) error

ForEachDependency executes a callback action on all the components, matching the target selector and dependencies, but only if selectors parameter is matching them.

func GenerateComponentsGraph

func GenerateComponentsGraph(fileprefix string, components []*Component) error

GenerateComponentsGraph generates dot and svg file including the selected components.

func GetTag

func GetTag[A any, Tag any](ball *Ball) (Tag, bool)

GetTag returns with attached tag (if attached).

func GetTagOf

func GetTagOf[Tag any](c *Component) (Tag, bool)

GetTagOf returns with attached tag (if attached).

func Implementation

func Implementation[L ~[]T, Instance any, T any](ball *Ball)

Implementation registers a new []T component, which will be filled with any registered instances. Instances will be marked with "Optional{}" tag, and will be injected only, if they are initialized. It's the responsibility of the Init code to exclude / include them during initialization.

func Initialize

func Initialize(ctx context.Context) func(c *Component) error

Initialize components as ForEach callback.

func MustGenerateGraph

func MustGenerateGraph(ball *Ball, fileprefix string, selector ComponentSelector)

MustGenerateGraph generates dot and svg files from components selected by the selector.

func MustLookup

func MustLookup[T any](ball *Ball) T

MustLookup returns with the registered component instance (or panic).

func Provide

func Provide[A any](ball *Ball, factory interface{})

Provide registers a new instance to the dependency pool. Run/Close methods are auto-detected (stage is created if they exist).

func RegisterImplementation

func RegisterImplementation[L ~[]T, T any](ball *Ball)

RegisterImplementation registers the implementation interface, without adding concrete implementation.

func RegisterManual

func RegisterManual[T any](
	ball *Ball,
	factory func(ctx context.Context) (T, error),
)

RegisterManual registers a component manually. Most of the time you need either Provide or View instead of this.

func RemoveTag

func RemoveTag[A any, Tag any](ball *Ball)

RemoveTag removes all the Tag type of tags from the component.

func RunWithDependencies

func RunWithDependencies(ctx context.Context, ball *Ball, selector ComponentSelector) error

RunWithDependencies will init and run all components which are matched by the selector.

func Supply

func Supply[T any](ball *Ball, t T)

Supply registers and instance which is already initialized.

func Tag

func Tag[A any, Tag any](ball *Ball, tag Tag)

Tag attaches a tag to the component registration.

func Tagged

func Tagged[Tag any]() func(c *Component) bool

Tagged is a selector, checking an annotation key/value.

func View

func View[From any, To any](ball *Ball, convert func(From) To)

View is lightweight component, which provides a type based on a existing instances.

Types

type Ball

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

Ball is the component registry.

Example
ball := NewBall()
Provide[string](ball, func() string {
	return "test"
})
Provide[string](ball, func() string {
	return "test"
})
components := Find(ball, All)
_ = components[0].Init(context.Background())
fmt.Println(components[0].Instance())
Output:

func NewBall

func NewBall() *Ball

NewBall creates a new component registry.

type Component

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

Component manages the lifecycle of a singleton Golang struct.

func Find

func Find(ball *Ball, selector ComponentSelector) (result []*Component)

Find selects components matching the selector.

func FindSelectedWithDependencies

func FindSelectedWithDependencies(ball *Ball, selector ComponentSelector) (result []*Component)

FindSelectedWithDependencies selects components matching the selector, together with all the dependencies.

func MustLookupComponent

func MustLookupComponent[T any](ball *Ball) *Component

MustLookupComponent gets the component (or panics if doesn't exist) based on a type.

func (*Component) Close

func (c *Component) Close(ctx context.Context) error

Close calls the Close stage function.

func (*Component) GetTarget

func (c *Component) GetTarget() reflect.Type

GetTarget returns with type, which is used as n identifier in mud.

func (*Component) ID

func (c *Component) ID() string

ID is the unque identifier of the component.

func (*Component) Init

func (c *Component) Init(ctx context.Context) error

Init initializes the internal singleton instance.

func (*Component) Instance

func (c *Component) Instance() any

Instance returns the singleton instance of the component. Can be null, if not yet initialized.

func (*Component) Name

func (c *Component) Name() string

Name returns with the human friendly name of the component.

func (*Component) Run

func (c *Component) Run(ctx context.Context, eg *errgroup.Group) error

Run executes the Run stage function.

func (*Component) String

func (c *Component) String() string

String returns with a string representation of the component.

type ComponentSelector

type ComponentSelector func(c *Component) bool

ComponentSelector can filter components.

func And

func And(selectors ...ComponentSelector) ComponentSelector

And selects components which matches all the selectors.

func ImplementationOf

func ImplementationOf[L ~[]T, T any](ball *Ball) ComponentSelector

ImplementationOf is a ForEach filter to get all the dependency of an implementation.

func Or

func Or(selectors ...ComponentSelector) ComponentSelector

Or selects components which matches any of the selectors.

func Select

func Select[A any](ball *Ball) ComponentSelector

Select is a component selector based on the specified type ([A]).

type Optional

type Optional struct{}

Optional tag is used to mark components which may not required.

type Stage

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

Stage represents a function which should be called on the component at the right time (like start, stop, init).

type StageName

type StageName string

StageName is the unique identifier of the stages (~lifecycle events).

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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