Dependency Injection

Wire is a compile-time dependency injection tool.

It is recommended that doing explicit initialization rather than using global variables.

Generating the initialization codes by Wire can reduce the coupling among components and increase the maintainability of the project.

Installation

  1. # Import into project
  2. go get -u github.com/google/wire
  3. # Install cmd
  4. go install github.com/google/wire/cmd/wire

Terms

There are two basic terms in wire, Provider and Injector.

Provider is a Go Func, it can also receive the values from other Providers for dependency injection.

  1. // provides a config file
  2. func NewConfig() *conf.Data {...}
  3. // provides the data component (the initialization of database, cache and etc.) which depends on the data config.
  4. func NewData(c *conf.Data) (*Data, error) {...}
  5. // provides persistence components (implementation of CRUD persistence) which depends on the data component.
  6. func NewUserRepo(d *data.Data) (*UserRepo, error) {...}

Usage

In Kratos project, there are four major modules, server, service, biz and data. They will be initialized by Wire.

kratos ddd

A ProviderSet should be provided in every module so that wire could scan them and generate the DI codes.

First, you should define ProviderSet in the entry of every module. The

  1. -data
  2. --data.go // var ProviderSet = wire.NewSet(NewData, NewGreeterRepo)
  3. --greeter.go // func NewGreeterRepo(data *Data, logger log.Logger) biz.GreeterRepo {...}

Then put these ProviderSet in the wire.go for DI configuration.

Component Initialization

wire.go is required for DI. The kratos application is for lifecycle management.

  1. // the entry point of the application
  2. cmd
  3. -main.go
  4. -wire.go
  5. -wire_gen.go
  6. // main.go creates the kratos application for lifecycle management.
  7. func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server, greeter *service.GreeterService) *kratos.App {
  8. pb.RegisterGreeterServer(gs, greeter)
  9. pb.RegisterGreeterHTTPServer(hs, greeter)
  10. return kratos.New(
  11. kratos.Name(Name),
  12. kratos.Version(Version),
  13. kratos.Logger(logger),
  14. kratos.Server(
  15. hs,
  16. gs,
  17. ),
  18. )
  19. }
  20. // wire.go initialization
  21. func initApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, error) {
  22. // builds ProviderSet in every modules, for the generation of wire_gen.go
  23. panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
  24. }

run go generate command in main directory to generate DI codes.

  1. go generate ./...

References