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 ¶
- func All(_ *Component) bool
- func CloseAll(ball *Ball, timeout time.Duration) error
- func DependsOn[BASE any, DEPENDENCY any](ball *Ball)
- func Dereference[A any](a *A) A
- func Dot(w io.Writer, components []*Component) (err error)
- func DotAll(w io.Writer, ball *Ball) (err error)
- func Execute[A any](ctx context.Context, ball *Ball, factory interface{}) (A, error)
- func Execute0(ctx context.Context, ball *Ball, factory interface{}) error
- func ForEach(ball *Ball, callback func(component *Component) error, ...) error
- func ForEachDependency(ball *Ball, target ComponentSelector, ...) error
- func GenerateComponentsGraph(fileprefix string, components []*Component) error
- func GetTag[A any, Tag any](ball *Ball) (Tag, bool)
- func GetTagOf[Tag any](c *Component) (Tag, bool)
- func Implementation[L ~[]T, Instance any, T any](ball *Ball)
- func Initialize(ctx context.Context) func(c *Component) error
- func MustGenerateGraph(ball *Ball, fileprefix string, selector ComponentSelector)
- func MustLookup[T any](ball *Ball) T
- func Provide[A any](ball *Ball, factory interface{})
- func RegisterImplementation[L ~[]T, T any](ball *Ball)
- func RegisterManual[T any](ball *Ball, factory func(ctx context.Context) (T, error))
- func RemoveTag[A any, Tag any](ball *Ball)
- func RunWithDependencies(ctx context.Context, ball *Ball, selector ComponentSelector) error
- func Supply[T any](ball *Ball, t T)
- func Tag[A any, Tag any](ball *Ball, tag Tag)
- func Tagged[Tag any]() func(c *Component) bool
- func View[From any, To any](ball *Ball, convert func(From) To)
- type Ball
- type Component
- func (c *Component) Close(ctx context.Context) error
- func (c *Component) GetTarget() reflect.Type
- func (c *Component) ID() string
- func (c *Component) Init(ctx context.Context) error
- func (c *Component) Instance() any
- func (c *Component) Name() string
- func (c *Component) Run(ctx context.Context, eg *errgroup.Group) error
- func (c *Component) String() string
- type ComponentSelector
- type Optional
- type Stage
- type StageName
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DependsOn ¶
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 ¶ added in v1.103.2
Dot generates graph report of the modules in dot format, but only the selected components are included.
func Execute ¶
Execute executes a function with injecting all the required dependencies with type based Dependency Injection.
func Execute0 ¶
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 ¶ added in v1.103.2
GenerateComponentsGraph generates dot and svg file including the selected components.
func Implementation ¶ added in v1.102.2
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 ¶
Initialize components as ForEach callback.
func MustGenerateGraph ¶ added in v1.103.2
func MustGenerateGraph(ball *Ball, fileprefix string, selector ComponentSelector)
MustGenerateGraph generates dot and svg files from components selected by the selector.
func MustLookup ¶
MustLookup returns with the registered component instance (or panic).
func Provide ¶
Provide registers a new instance to the dependency pool. Run/Close methods are auto-detected (stage is created if they exist).
func RegisterImplementation ¶ added in v1.103.2
RegisterImplementation registers the implementation interface, without adding concrete implementation.
func RegisterManual ¶
RegisterManual registers a component manually. Most of the time you need either Provide or View instead of this.
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.
Types ¶
type Ball ¶
type Ball struct {
// contains filtered or unexported fields
}
Ball is the component registry.
Example ¶
Output:
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 ¶ added in v1.102.2
MustLookupComponent gets the component (or panics if doesn't exist) based on a type.
func (*Component) Instance ¶
Instance returns the singleton instance of the component. Can be null, if not yet initialized.
type ComponentSelector ¶
ComponentSelector can filter components.
func And ¶
func And(selectors ...ComponentSelector) ComponentSelector
And selects components which matches all the selectors.
func ImplementationOf ¶ added in v1.102.2
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.