bindly

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2025 License: Apache-2.0 Imports: 18 Imported by: 0

README

Bindaly - Golang Dependency Injection

GoReportCard GoDoc

Bindaly is a powerful, flexible dependency injection library for Go that helps manage application component dependencies with minimal boilerplate and maximal type safety.

Introduction

Bindaly provides a straightforward way to inject dependencies into Go structs using struct tags. It works with generic types to provide both flexibility and type safety, and supports caching, transformations, and complex dependency graphs.

Features

  • Type-safe dependency injection with generics
  • Annotation-based binding with struct tags
  • Value transformations via custom transformers
  • Intelligent type conversion between compatible types
  • Value caching for performance optimization
  • Fully concurrent-safe operations
  • Extensible architecture with custom providers and locators

Installation

go get github.com/viant/binder

Usage

Basic Example

package main

import (
	"context"
	"fmt"
	"github.com/viant/bindly"
	"github.com/viant/bindly/locator/buildin"
	"github.com/viant/structology"
)

type AppConfig struct {
	ServerPort int
	BaseURL    string
}

// Logger is a simple logging service
type Logger interface {
	Log(message string)
}

// SimpleLogger implements Logger
type SimpleLogger struct{}

func (l *SimpleLogger) Log(message string) {
	fmt.Println(message)
}

type DependencySetup struct {
	Config     *AppConfig
	Settings   map[string]interface{}
	Interfaces map[string]interface{}
}

// Service uses the configuration
type Service struct {
	Debug      bool   `bind:"kind=setting,in=debug"`
	ServerPort int    `bind:"in=Config.ServerPort"` //state kind is a default kind
	Logger     Logger //interface bind by default to interface kind
}

func main() {

	var iLogger Logger
	appLoger := &SimpleLogger{}

	setup := &DependencySetup{
		Config: &AppConfig{
			ServerPort: 8080,
			BaseURL:    "http://localhost:8080",
		},

		Interfaces: map[string]interface{}{
			structology.InterfaceTypeOf(&iLogger).String(): appLoger,
		},
		Settings: map[string]interface{}{
			"debug": true,
			"port":  8080,
		},
	}

	var opts = append([]bindly.InjectorOption{}, bindly.WithProviders(
		buildin.Struct("state", "", 1),
		buildin.Map("setting", "Settings", 1),
		buildin.Map("interface", "Interfaces", 1)))

	injector := bindly.NewInjector(opts...)
	service := &Service{}
	err := bindly.WithState[Service](injector, setup).Inject(context.Background(), service)
	fmt.Println(service, err)
}

Binding with Tags

Bindaly uses struct tags to define dependencies:

type MyStruct struct {
    // Inject from state with key "config.port"
    Port int `bind:"in=config.port"`
    
    // Inject from a specific provider kind
    Database *Database `bind:"kind=database,in=primary"`
    
    // Cache the resolved value
    ExpensiveData []Item `bind:"kind=service,in=data,cacheable"`
    
    // Transform values during injection
    ConfigValue string `bind:"in=rawValue" xform:"string"`
}

Value Transformers

Transformers convert values during injection:

// Register custom transformers
transformerRegistry := xform.NewRegistry()
xform.conv.Init(transformerRegistry)  // Initialize standard converters
transformerRegistry.Register("custom", xform.NewTransformerFactory("custom", NewCustomTransformer))

injector := bindly.New(
    bindly.WithTransformers(transformerRegistry),
)

Advanced Features

Caching Values

// Create a value cache
cache := bindly.NewValueCache()

// Use cache with binding context
bindingCtx := bindly.WithState[MyService](
    injector, 
    state,
    bindly.WithCache[MyService](cache),
)

// Save cache to disk
err := cache.Save(ctx, "/path/to/cache.bin")

// Load cache from disk
err := cache.Load(ctx, "/path/to/cache.bin")

Custom Providers

// Implement custom provider
type MyProvider struct {
    // provider implementation
}

func (p *MyProvider) Locate(state *structology.State) locator.Locator {
    // Return appropriate locator based on state
}

// Register provider
injector := bindly.New(
    bindly.WithProviders(&MyProvider{}),
)

Performance Considerations

  • Use cacheable for expensive operations
  • Prefer direct binding over transformations when possible
  • Consider pre-building binding types for frequently used structs

Thread Safety

The binder library is designed to be fully thread-safe. All caching operations use appropriate locking mechanisms to ensure safe concurrent access.

License

The source code is made available under the terms of the Apache License, Version 2.0. See the LICENSE file for more details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Documentation

Overview

Example (Inject)
package main

import (
	"context"
	"fmt"
	"github.com/viant/bindly"
	"github.com/viant/bindly/locator/buildin"
	"github.com/viant/structology"
)

type AppConfig struct {
	ServerPort int
	BaseURL    string
}

// Logger is a simple logging service
type Logger interface {
	Log(message string)
}

// SimpleLogger implements Logger
type SimpleLogger struct{}

func (l *SimpleLogger) Log(message string) {
	fmt.Println(message)
}

type DependencySetup struct {
	Config     *AppConfig
	Settings   map[string]interface{}
	Interfaces map[string]interface{}
}

// Service uses the configuration
type Service struct {
	Debug      bool   `bind:"kind=setting,in=debug"`
	ServerPort int    `bind:"in=Config.ServerPort"` //state kind is a default kind
	Logger     Logger //interface bind by defaul to interface kind
}

func main() {

	var iLogger Logger
	appLoger := &SimpleLogger{}

	dependencies := &DependencySetup{
		Config: &AppConfig{
			ServerPort: 8080,
			BaseURL:    "http://localhost:8080",
		},

		Interfaces: map[string]interface{}{
			structology.InterfaceTypeOf(&iLogger).String(): appLoger,
		},
		Settings: map[string]interface{}{
			"debug": true,
			"port":  8080,
		},
	}

	var opts = append([]bindly.InjectorOption{}, bindly.WithProviders(
		buildin.Struct("state", "", 1), //empty kind
		buildin.Map("setting", "Settings", 1),
		buildin.Map("interface", "Interfaces", 1)))

	injector := bindly.NewInjector(opts...)
	service := &Service{}
	err := bindly.WithState[Service](injector, dependencies).Inject(context.Background(), service)

	fmt.Println(service, err)
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Binding

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

Binding represents a binding

type BindingCache

type BindingCache struct {
	internal.Map[reflect.Type, *BindingType]
}

func NewBindingCache

func NewBindingCache() *BindingCache

type BindingContext

type BindingContext[T any] struct {
	// contains filtered or unexported fields
}

BindingContext represents binding context with state

func WithState

func WithState[T any](binder *Injector, state interface{}, opt ...BindingOption[T]) *BindingContext[T]

func (*BindingContext[T]) Inject

func (c *BindingContext[T]) Inject(ctx context.Context, target *T) error

Inject binds dependencies to the target

func (*BindingContext[T]) Value

func (c *BindingContext[T]) Value(ctx context.Context, location *state.Location) (interface{}, bool, error)

type BindingOption

type BindingOption[T any] func(ctx *BindingContext[T])

func WithCache

func WithCache[T any](cache *ValueCache) BindingOption[T]

type BindingType

type BindingType struct {
	Bindings []Bindings
	Type     *structology.StateType
}

type Bindings

type Bindings []*Binding

func (Bindings) GroupByPriority

func (b Bindings) GroupByPriority(registry *locator.Registry) ([]Bindings, error)

type Injector

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

Injector represents dependency injector

func NewInjector

func NewInjector(options ...InjectorOption) *Injector

NewInjector creates injector

func (*Injector) TransformerRegistry

func (b *Injector) TransformerRegistry() *xform.Registry

TransformerRegistry returns the transformer registry

type InjectorOption

type InjectorOption func(*Injector)

func WithBindingTag

func WithBindingTag(tag string) InjectorOption

func WithLocators

func WithLocators(registry *locator.Registry) InjectorOption

func WithProviders

func WithProviders(providers ...locator.Provider) InjectorOption

func WithTransformerTag

func WithTransformerTag(tag string) InjectorOption

type StructTypeCache

type StructTypeCache struct {
	internal.Map[reflect.Type, *structology.StateType]
}

func NewStructTypeCache

func NewStructTypeCache() *StructTypeCache

type ValueCache

type ValueCache struct {
	internal.Map[string, interface{}]
	// contains filtered or unexported fields
}

func NewValueCache

func NewValueCache() *ValueCache

func (*ValueCache) Clear

func (c *ValueCache) Clear()

Clear empties the cache

func (*ValueCache) Load

func (c *ValueCache) Load(ctx context.Context, URL string) error

Load loads the cache from disk

func (*ValueCache) Save

func (c *ValueCache) Save(ctx context.Context, destURL string) error

Save persists the cache to disk

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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