简体   繁体   中英

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 !

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
}

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.

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)

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)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM