Vincent
Vincent

Reputation: 472

What is the best way to have dependency injection in Golang

I'm a beginner in Golang and I'm working on a small library which need to get a DB connection at some point in the code for différent sub package / method call. I'm just wondering how I can manage this ?

Example, If I manage to have a webserver, it works with handler, so how can I get this connection inside this function ? It could be used with another process, simple method call or MVC model ?

I don't want to use global because for me it's a bad practice except if it's very exceptional way (or tricky somehow).

I read a lot of write in different website, but still, I'm asking and learning from different opinion and experiences.

Thanks for your time !

Upvotes: 9

Views: 11558

Answers (5)

Milad Rahimi
Milad Rahimi

Reputation: 3834

For an IoC container, you can try this package: https://github.com/golobby/container

Example of singleton binding:

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

Example of resolving:

var db Database
container.Make(&db)

As you can see it's so easy to work with.

Upvotes: 0

John Deng
John Deng

Reputation: 29

You can also try Hiboot which is a web/cli application framework that support dependency injection out of box.

Docs

// HelloService is a simple service interface, with interface, we can mock a fake service in unit test
type HelloService interface {
    SayHello(name string) string
}

type helloServiceImpl struct {
}

func init() {
    // Register Rest Controller through constructor newHelloController
    // Register Service through constructor newHelloService
    app.Register(newHelloController, newHelloService)
}

// please note that the return type name of the constructor HelloService,
// hiboot will instantiate a instance named helloService for dependency injection
func newHelloService() HelloService {
    return &helloServiceImpl{}
}

// SayHello is a service method implementation
func (s *helloServiceImpl) SayHello(name string) string {
    return "Hello" + name
}

// PATH: /login
type helloController struct {
    web.Controller
    helloService HelloService
}

// newHelloController inject helloService through the argument helloService HelloService on constructor
func newHelloController(helloService HelloService) *helloController {
    return &helloController{
        helloService: helloService,
    }
}

// Get /
// The first word of method name is the http method GET
func (c *helloController) Get(name string) string {
    return c.helloService.SayHello(name)
}

Upvotes: 1

jwells131313
jwells131313

Reputation: 2394

I would suggest Dargo which is an injection engine in the style of Java CDI and/or JSR-330. It uses struct annotations and Injection is performed using reflection or using creator functions. It supports different lifecycles for services including Singleton (created exactly once, lazily), PerLookup (created every time injected or looked up), Immediate (created exactly once, eagerly) and DargoContext (tied to the lifecycle of a context.Context)

Upvotes: 1

Alessandro Dionisi
Alessandro Dionisi

Reputation: 2684

I would suggest giving a try to https://github.com/facebookgo/inject. It allows to define an object graph and specify dependencies with struct annotations. Injection is performed using reflection.

Upvotes: 0

ZAky
ZAky

Reputation: 1307

Create a struct that represent the resource​, let's call Cart. Add get and post methods to this struct. These methods should be http handlers. In main create an instance of the struct with db interface. And in the route call Cart.get. Now in get method you have access to the db interface.

Not a working example, just to get the idea of injecting for testing.

type storage interface {
    PrepareContext(context.Context, string) (*sql.Stmt, error)
}

func main() {
    db, _ := sql.Open("mysql", `queryString`)
    http.HandleFunc("/", Cart{db}.get)
    http.ListenAndServe(":8080", nil)
}

type Cart struct {
    storage
}

func (crt Cart) get(w http.ResponseWriter, r *http.Request) {
    q, _ := crt.PrepareContext(context.Background(), `select *`)
    fmt.Println(q.Exec())
}

/////////Test
type testDB struct{}

func (c testDB) PrepareContext(context.Context, string) (*sql.Stmt, error) {
    return nil, nil
}
func TestGet(t *testing.T) {
    db := testDB{}
    _ = Cart{db}

    //http test here
}

Upvotes: 3

Related Questions