A lightweight yet powerful IoC container for Go projects

Overview

GoDoc Build Status Go Report Card Awesome Coverage Status

Container

A lightweight yet powerful IoC container for Go projects. It provides a simple, fluent and easy-to-use interface to make dependency injection in GoLang easier.

Documentation

Required Go Versions

It requires Go v1.11 or newer versions.

Installation

To install this package, run the following command in the root of your project.

go get github.com/golobby/container

Introduction

GoLobby Container like any other IoC container is used to bind abstractions to their implementations. Binding is a process of introducing an IoC container that which concrete (implementation) is appropriate for an abstraction. In this process, you also determine how it must be resolved, singleton or transient. In singleton binding, the container provides an instance once and returns it for each request. In transient binding, the container always returns a brand new instance for each request. After the binding process, you can ask the IoC container to get the appropriate implementation of the abstraction that your code depends on. In this case, your code depends on abstractions, not implementations.

Binding

Singleton

Singleton binding using Container:

err := container.Singleton(func() Abstraction {
  return Implementation
})

It takes a resolver function which its return type is the abstraction and the function body configures the related concrete (implementation) and returns it.

Example for a singleton binding:

err := container.Singleton(func() Database {
  return &MySQL{}
})

Transient

Transient binding is also similar to singleton binding.

Example for a transient binding:

err := container.Transient(func() Shape {
  return &Rectangle{}
})

Resolving

Container resolves the dependencies with the method make().

Using References

One way to get the appropriate implementation you need is to declare an instance of the abstraction type and pass its reference to Container this way:

var a Abstraction
err := container.Make(&a)
// "a" will be implementation of the Abstraction

Example:

var m Mailer
err := container.Make(&m)
if err != nil {
    panic(err)
}

m.Send("[email protected]", "Hello Milad!")

Using Closures

Another way to resolve the dependencies is by using a function (receiver) that its arguments are the abstractions you need. Container will invoke the function and pass the related implementations for each abstraction.

err := container.Make(func(a Abstraction) {
  // "a" will be implementation of the Abstraction
})

Example:

err := container.Make(func(db Database) {
  // "db" will be the instance of MySQL
  db.Query("...")
})

You can also resolve multiple abstractions this way:

err := container.Make(func(db Database, s Shape) {
  db.Query("...")
  s.Area()
})

Binding time

You can also resolve a dependency at the binding time in your resolver function like the following example.

// Bind Config to JsonConfig
err := container.Singleton(func() Config {
    return &JsonConfig{...}
})

// Bind Database to MySQL
err := container.Singleton(func(c Config) Database {
    // "c" will be the instance of JsonConfig
    return &MySQL{
        Username: c.Get("DB_USERNAME"),
        Password: c.Get("DB_PASSWORD"),
    }
})

Notice: You can only resolve the dependencies in a binding resolver function that has already bound.

Standalone instance

Container works without any initialization keeping your bindings in the default instance. Sometimes you may want to create a standalone instance for a part of application. If so, create a new instance:

c := container.NewContainer() // returns container.Container
_ = c.Singleton(binding)
_ = c.Make(&resolver)

The rest stays the same. The default container is still available.

Usage Tips

Performance

The package Container inevitably uses reflection in binding and resolving processes. If performance is a concern, you should use this package more carefully. Try to bind and resolve the dependencies out of the processes that are going to run many times (for example, on each request), put it where that run only once when you run your applications like main and init functions.

License

GoLobby Container is released under the MIT License.

Issues
  • Support named bindings

    Support named bindings

    This allows to create many bindings with different identifiers for the same interface. This is mostly helpful when implementing abstract factories where identifiers of concrete factories are stored somewhere as strings.

    opened by outofforest 6
  • added generic ResolveT and

    added generic ResolveT and "must" methods

    This PR adds the following methods:

    c := container.New()
    
    db, err := container.ResolveT[Database](c)
    if err != nil {
        // error
    }
    
    db := container.MustResolveT[Database](c)
    
    container.MustSingleton(c, resolver)
    container.MustNamedSingleton(c, "", resolver)
    container.MustTransient(c, resolver)
    container.MustNamedTransient(c, "", resolver)
    container.MustCall(c, receiver)
    
    opened by Place1 5
  • Get the receiver/instance from the concrete / AutoBind based on tags

    Get the receiver/instance from the concrete / AutoBind based on tags

    Hi, I am working on a project where we use the container library.

    We are trying to "tune" the usage of the DI container with custom tags, to get rid of call-chains like

    func NewInstance() *Instance {
        var repo repository.Notifications
        var bridge fcm.Bridge
        var timeProvider tp.TimeProvider
    
        main.DI.Make(&repo)
        main.DI.Make(&bridge)
        main.DI.Make(&timeProvider)
    
        // return instance...
    }
    

    What I want to do is to mark some struct fields with a custom tag, let's say di:"inject" to automatically inject the fields at some point (like execute call).

    What I've already achieved is the following code:

    type Instance struct {
        context ctx.Context
        
        repo repository.Notifications `di:"bind"`
        ...
    }
    
    func inject(gc GoCase) {
    	rv := reflect.ValueOf(gc).Elem()
    	t := rv.Type()
    
    	for i := 0; i < t.NumField(); i++ {
    		field := t.Field(i)
    		rvField := rv.Field(i)
    		if value, ok := field.Tag.Lookup("di"); ok {
    			if value == "bind" {
    
    				y := reflect.NewAt(rvField.Type(), unsafe.Pointer(rvField.UnsafeAddr())).Elem()
    				receiverType := y.Type()
    				concrete, ok := DI[receiverType] // DI is the app-container
    				if !ok {
    					panic("concrete not found for the abstraction" + receiverType.String())
    				}
    
    				// Concrete found, but how do I get instance/receiver from the concrete?
    				// Do I need to use the reflection here? Or maybe it's better to add/expose
    				// a possibility to do that in the library?
    				
    				fmt.Print(concrete) // just for testing
    			}
    		}
    	}
    }
    

    My wonderings are explained in the comment.

    And I see a few possibilities:

    • use reflection to find the concrete (doesn't sound good to me, to be honest)
    • expose proper fields or add a proper method in the library (like GetConcrete(Type))
    • add an "extension" to the library that allows doing the bindings based on a tag, let's say di:"inject" (I can implement it, but first I would like to know what you think about it).
    • or, maybe you see another way to do that? :)

    Thanks for your input!

    opened by Cililing 5
  • Error returning except panic

    Error returning except panic

    In case of any error on this package, we decide to use the built-in panic() function that stops the normal execution of the current goroutine. Based on golang builtin.go we have:

    ... This continues until all functions in the executing goroutine have stopped, in reverse order. At that point, the program is terminated with a non-zero exit code. This termination sequence is called panicking and can be controlled by the built-in function recover.

    As an injected part of any project, it's not a good deal to call panic() and terminate the program with a non-zero exit code. Also, there is no place to explain this important notice to users that they can make any recovery plan for error situations! I believe Panics are not exceptions and I think it's better to returning an error on error cases and removing panic() in all of the repo. And panic is useful for sometimes the program simply cannot continue. The library has not this ownership. The regexp.MustCompile() maybe can a good example of something that may panic. On the other hand, Golang built-in libs used panic in internal packages but it recovers panic in itself too! Take a look on json.marshal().

    And I think this link prepared a good article for using panics in projects. Advanced Go Panic Programming

    Maybe it's not bad to remember that error handling is one of the in-progress improvements on Golang Go 2 Draft Designs

    opened by sedhossein 3
  • How we can get property of an implementation?

    How we can get property of an implementation?

    I am using golobby/container/v3 :

    type Database interface {
    }
    
    type DatabaseImpl struct {
        MySql interface{}
        PgSql interface{}
    }
    
    container.Singleton(func() Database {
        return &DatabaseImpl {
            MySql: mysql.CreateConnection(),
            PgSql: pgsql.CreateConnection(),
        }
    })
    

    Now I want to get an instance of MySql connection, so how can I get it?

    I try to do:

    var d Database
    container.Resolve(&d)
    
    result, _ := d.MySql.Query(...)
    

    but i get an error type Database has no field or method MySql

    opened by YogiPristiawan 2
  • Singleton's are created twice when resolver returns (T, error)

    Singleton's are created twice when resolver returns (T, error)

    i've added some example code below and i've found that the singleton is created twice.

    c := container.New()
    
    c.Singleton(func() (ExampleService, error) {
    	fmt.Println("singleton created")
    	return CreateExampleService()
    })
    

    the issue only occurs because the resolver function returns a 2-tuple of (ExampleService, error)

    the issue is happening because of the invoke call happening in this loop (I think) https://github.com/golobby/container/blob/master/container.go#L101

    bug 
    opened by Place1 2
  • Singleton always cast second parameter as error

    Singleton always cast second parameter as error

    First of all, thanks for this amazing library. Here is what my singleton call is like

    container.Singleton(func() (*gorm.DB, error) {
            dbFile := "/tmp/test.db"
    	dialector := sqlite.Open(dbFile)
    	db, err := gorm.Open(dialector)
    
    	if err != nil {
    		return nil, err
    	}
    
    	return db.Debug(), nil
    })
    

    But I am always getting panic: interface conversion: interface is nil, not error even when error is nil.

    I think there should be a check for nil before casting to error type in https://github.com/golobby/container/blob/800c8e19e5cc8d7e9471a1b9faf66367294d5e55/pkg/container/container.go#L74

    Can you please look into it?

    Thanks

    opened by iarkaroy 2
  • Enable to Get Error on Binding

    Enable to Get Error on Binding

    Hello, I am using your great library for Dependency injection.
    However, I want to retrieve errors when binding.
    For instance, my code below is to open SQLite DB, but when Opening DB failed, I have to call panic() to show an error.

    container.Singleton(func() *gorm.DB {
    		DBFile := "/tmp/test.db"
    		dialector := sqlite.Open(DBFile)
    		db, err := gorm.Open(dialector)
    
    		if err != nil {
    			panic(err)
    		}
    
    		return db.Debug()
    	})
    

    So, I want you to enable me to run the code like below to retrieve the error.
    Right now, when error occurs, the code below just cause nil reference error.

    container.Singleton(func() (*gorm.DB, error) {
    		DBFile := "/tmp/test.db"
    		dialector := sqlite.Open(DBFile)
    		db, err := gorm.Open(dialector)
    
    		if err != nil {
    			return nil, err
    		}
    
    		return db.Debug(), nil
    	})
    

    Thanks,

    opened by forest1102 2
  • will this project detect circular dependency?

    will this project detect circular dependency?

    i am looking for an ioc container that can solve circular dependency.

    i cannot find source code which do detect circular dependency from the project.

    anyway, this project design is a very useful example for me, thank you!

    opened by zjsxwc 2
  • support unexported fields

    support unexported fields

    The current implementation doesn't support unexported fields. While trying to Fill a struct with tagged unexported field we get:

    panic: reflect: reflect.Value.Set using value obtained using unexported field [recovered]
            panic: reflect: reflect.Value.Set using value obtained using unexported field
    

    I think it's required to support such fields. Very often we don't want to expose struct fields like database and hide it as the internal definition injected by the framework.

    This pull-request adds such support. The added test is basically the same as the exiting one, but with unexported fields.

    opened by Cililing 1
  • Add license scan report and status

    Add license scan report and status

    Your FOSSA integration was successful! Attached in this PR is a badge and license report to track scan status in your README.

    Below are docs for integrating FOSSA license checks into your CI:

    opened by fossabot 1
  • Lazy singleton construction

    Lazy singleton construction

    This PR makes singleton construction lazy. It also adds a lock to each container binding so that multiple goroutines can safely work with the container at the same time.

    This feature is very helpful if you have multiple programs/entrypoints that share the same container but only use a subset of the dependencies.

    In my use-case I have a server and cli application that share the same container; there are many shared dependencies but not everything is used by both programs. For example the TLS configuration used by the server isn't used by the cli application and attempting to resolve it will fail because the certificates won't be available.

    With this PR the problem is solved because only the dependencies (bindings) that are requested are constructed; if the cli app never requests the TLS configuration then it's resolver is never executed.

    opened by Place1 6
Releases(v3.2.1)
Owner
GoLobby
GoLobby publishes open source packages with simplicity and fluent APIs in mind for GoLang
GoLobby
An additive dependency injection container for Golang.

Alice Alice is an additive dependency injection container for Golang. Philosophy Design philosophy behind Alice: The application components should not

Minjie Zha 45 Jun 15, 2022
🛠 A full-featured dependency injection container for go programming language.

DI Dependency injection for Go programming language. Tutorial | Examples | Advanced features Dependency injection is one form of the broader technique

Goava 151 Jun 16, 2022
Simple Dependency Injection Container

?? gocontainer gocontainer - Dependency Injection Container ?? ABOUT Contributors: Rafał Lorenz Want to contribute ? Feel free to send pull requests!

Rafał Lorenz 15 Mar 10, 2022
Serverless Container Workflows

direktiv event-based serverless container workflows Check out our online demo: wf.direktiv.io What is Direktiv? Direktiv is a specification for a serv

vorteil.io pty ltd 195 Jun 29, 2022
A lightweight yet powerful config package for Go projects

Config GoLobby Config is a lightweight yet powerful config package for Go projects. It takes advantage of env files and OS variables alongside config

GoLobby 284 Jun 15, 2022
Simple, yet powerful Adcell go client to import data feeds into you projects.

adcell-go Simple, yet powerful Adcell go client to import data feeds into you projects. Explore the docs » View Demo · Report Bug · Request Feature Ta

Matthias Bruns 0 Oct 31, 2021
基于 IoC 的 Go 后端一站式开发框架 🚀

Go-Spring 的愿景是让 Go 程序员也能用上如 Java Spring 那般威力强大的编程框架。 其特性如下: 提供了完善的 IoC 容器,支持依赖注入、属性绑定; 提供了强大的启动器框架,支持自动装配、开箱即用; 提供了常见组件的抽象层,支持灵活地替换底层实现; Go-Spring 当前使

DiDi 97 Jun 17, 2022
Simple and yet powerful Dependency Injection for Go

goioc/di: Dependency Injection Why DI in Go? Why IoC at all? I've been using Dependency Injection in Java for nearly 10 years via Spring Framework. I'

Go IoC 187 Jun 20, 2022
Fast, powerful, yet easy to use template engine for Go. Optimized for speed, zero memory allocations in hot paths. Up to 20x faster than html/template

quicktemplate A fast, powerful, yet easy to use template engine for Go. Inspired by the Mako templates philosophy. Features Extremely fast. Templates

Aliaksandr Valialkin 2.5k Jun 22, 2022
Simple Yet Powerful Logger

sypl sypl provides a Simple Yet Powerful Logger built on top of the Golang sypl. A sypl logger can have many Outputs, and each Output is responsible f

Sauce Labs 9 Apr 28, 2022
Fast and powerful Git hooks manager for any type of projects.

Lefthook The fastest polyglot Git hooks manager out there Fast and powerful Git hooks manager for Node.js, Ruby or any other type of projects. Fast. I

Abroskin Alexander 2.1k Jun 27, 2022
Powerful CLI written in GO to generate projects in various technologies

Barca CLI is a project generator written in GO and its purpose is to build and configure HTTP servers, web proxy, SPA/PWA, Blog and custom landing page. It's easy, fast and productive.

Barca 1 Jun 3, 2022
Clones github projects into ~/Projects/github/{org}/{repo}

Tidy clone Github cli extension (gh extension) to clone repos into ~/Projects/github/{org}/{repo} on the local filesystem Install gh extension install

Pascal Welsch 1 Jan 19, 2022
xyr is a very lightweight, simple and powerful data ETL platform that helps you to query available data sources using SQL.

xyr [WIP] xyr is a very lightweight, simple and powerful data ETL platform that helps you to query available data sources using SQL. Supported Drivers

Mohammed Al Ashaal 55 Apr 4, 2022
Best lightweight, powerful and really fast Api with Golang (Fiber, REL, Dbmate) PostgreSqL

Best lightweight, powerful and really fast Api with Golang (Fiber, REL, Dbmate) PostgreSqL

Elias Champi 1 Dec 26, 2021
Best simple, lightweight, powerful and really fast Api with Golang (Fiber, REL, Dbmate) PostgreSqL Database and Clean Architecture

GOLANG FIBER API (CLEAN ARCHITECTURE) Best simple, lightweight, powerful and really fast Api with Golang (Fiber, REL, Dbmate) PostgreSqLDatabase using

Elias Champi 2 May 20, 2022
skr: The lightweight and powerful web framework using the new way for Go.Another go the way.

skr Overview Introduction Documents Features Install Quickstart Releases Todo Pull Request Issues Thanks Introduction The lightweight and powerful web

go-the-way 1 Jan 11, 2022
A lightweight but powerful OCR tool.

这个项目是什么? LOCR(Lightweight OCR)是一款轻量级的文字识别工具, 结合第三方截图工具, 可以快速的对图片文字进行识别。 为什么有这个项目 在日常学习的工作中, 难免会遇到一些文字的复制粘贴任务。但由于一些限制,我们无法复制想要的文字,只能一个字一个字的敲出来。而随着近几年OC

lingyin 5 May 24, 2022
Moby Project - a collaborative project for the container ecosystem to assemble container-based systems

The Moby Project Moby is an open-source project created by Docker to enable and accelerate software containerization. It provides a "Lego set" of tool

Moby 63.3k Jun 23, 2022
Yeqllo 22 Nov 26, 2021
Moby Project - a collaborative project for the container ecosystem to assemble container-based systems

The Moby Project Moby is an open-source project created by Docker to enable and accelerate software containerization. It provides a "Lego set" of tool

Moby 63.4k Jun 24, 2022
top in container - Running the original top command in a container

Running the original top command in a container will not get information of the container, many metrics like uptime, users, load average, tasks, cpu, memory, are about the host in fact. topic(top in container) will retrieve those metrics from container instead, and shows the status of the container, not the host.

silenceshell 69 Jun 20, 2022
Boxygen is a container as code framework that allows you to build container images from code

Boxygen is a container as code framework that allows you to build container images from code, allowing integration of container image builds into other tooling such as servers or CLI tooling.

nitric 5 Dec 13, 2021
Amazon ECS Container Agent: a component of Amazon Elastic Container Service

Amazon ECS Container Agent The Amazon ECS Container Agent is a component of Amazon Elastic Container Service (Amazon ECS) and is responsible for manag

null 0 Dec 28, 2021
The Container Storage Interface (CSI) Driver for Fortress Block Storage This driver allows you to use Fortress Block Storage with your container orchestrator

fortress-csi The Container Storage Interface (CSI) Driver for Fortress Block Storage This driver allows you to use Fortress Block Storage with your co

Fortress 0 Jan 23, 2022
Ixia-c-one - A re-packaged (as a single-container) flavor of multi-container application ixia-c

ixia-c-one ixia-c-one is a re-packaged (as a single-container) flavor of multi-c

Open Traffic Generator 3 Apr 1, 2022