Go: Сomparing dependency injection approaches

Konstantin Makarov
Dec 28, 2019 · 3 min read
Image for post
Image for post
ihippik ©

In this article, I wanted to discuss such a development pattern called dependency injection.
And on the example of the Go language, compare two approaches, namely, a couple of libraries that implement these approaches.

Dependency injection (DI) is the process of providing external dependency to a software component.

Let me quickly explain why this approach is needed for those who may not have heard about it.
Suppose we have a certain service that is configured by a certain config and works with the database through the repository abstraction:

type Service struct {
config *Config
repo Repository
}

We create an instance of this service for further work with it:

func NewService() *Service {
cfg:=LoadCfgFromFile("cfg.yml")
db,_:= DbConnect(cfg)
return &Service{
config: cfg,
repo: NewRepository(db)
}
}

Everything is fine. To create a new service you do not need anything except a configuration file!
But, what if tomorrow we want to get the configuration from the environment variables or Consul. Have to rewrite the service.
Then think about how we will test the repository? We will have to connect to a real database, instead of mock-testing!

As we all know, in order to write high-quality and well-tested code, we need to break the tight link between the software component
and required dependencies.
In our case, the dependencies will be passed to the specific component as the parameters of its constructor:

func NewService(config *Config, repo Repository) *Service {
return &Service{config: config, repo: repo}
}

And now, instead of the repository, we can pass the mock-object and easily test the service without connecting to a real database.
We can also fill out our config anywhere and anytime.
Now everything is convenient, but time is running out, our service is expanding, becoming more complicated, and similar services with their dependencies are being added to it.
From now on, you need to deal with the creation of all these dependencies in the right sequence and in the right quantities.
Create a configuration, create a connection to the database and pass it to the repository designer. Then all this is transferred to the constructor of the service.
This becomes a problem when the number of dependencies and the services themselves grows.

But happily, these problems have already been solved, we can only choose approach.

The main thing to know here is the concept of a container -
a place where providers our objects are added methods that create configurations and repositories objects and from where you will receive a Service object.
In dig, these are the provide and invoke methods, respectively.

The used technique is as follows:

  • Create a container
container := dig.New()
  • Add providers
container.Provide(NewConfig)
container.Provide(DbConnect)
container.Provide(NewRepository)
container.Provide(NewService)
  • Provide a finished object of Service
container.Invoke(func(server *Service) {
service.Start()
})

https://github.com/google/wire

Here the approach is similar, but there are differences in implementation:

  • We create wire.go in which we describe everything that we need to create our service
func InitializeService() *Service {
wire.Build(NewService, NewConfig, DbConnect, NewRepository)
return &Service{}
}
  • Using the wire utility, we generate everything that we needed to write with our hands
// wire_gen.go
func InitializeService() *Service {
cfg:= NewConfig()
db:= DbConnect(cfg)
repo:=NewRepository(db)
return NewService(cfg,repo)
}

You should choose, but in my opinion, the use of code generation has some advantages over reflection.
We learn about all the problems at the compilation time and we will also do all the dirty work in advance!

Rob Pike: Reflection is never clear

I advise you to turn to the Google library. Their detailed instructions will help you start using it as quickly as possible.

Scum-Gazeta

Technical notes and reflections on programming

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store