Config injection

Config injection is a specific implementation of method and parameter injection. With config injection, we combine multiple dependencies and system-level config and merge them into a config interface.

Consider the following constructor:

// NewLongConstructor is the constructor for MyStruct
func NewLongConstructor(logger Logger, stats Instrumentation, limiter RateLimiter, cache Cache, timeout time.Duration, workers int) *MyStruct {
return &MyStruct{
// code removed
}
}

As you can see, we are injecting multiple dependencies, including a logger, instrumentation, rate limiter, cache, and some configuration.

It is safe to assume that we would be likely to inject at least the logger and the instrumentation into most of our objects in this same project. This results in a minimum of two parameters for every constructor. Across an entire system, this adds up to a lot of extra typing. It also detracts from the UX of our constructors by making them harder to read, and this potentially hides the significant parameters among the common ones.

Consider for a moment—where are the values for timeout and the number of workers that are likely to be defined? They are probably defined from some central source, such as a config file.

By applying config injection, our example becomes the following:

// NewByConfigConstructor is the constructor for MyStruct
func NewByConfigConstructor(cfg MyConfig, limiter RateLimiter, cache Cache) *MyStruct {
return &MyStruct{
// code removed
}
}

We have merged the common concerns and the configuration together into the config definition but left the significant parameters intact. In this manner, the function parameters are still informative without having to read the config interface definition. In a way, we have hidden or encapsulated the common concerns.

There is another usability aspect to consider—the config is now an interface. We should think about what kind of object would implement such an interface. Does such an object already exist? What are its responsibilities?

Often config comes from a single source and its responsibilities are to load the config and provide access to it. Even though we are introducing the config interface to decouple from the actual config management, leveraging the fact that it's a single source is still convenient.

Consider the following code:

myFetcher := NewFetcher(cfg, cfg.URL(), cfg.Timeout())

This code indicates that all of the parameters are coming from the same place. This is a good indication that they can be merged.

If you come from an object-oriented background, you may be familiar with the concept of a service locator. Config injection is intentionally very similar. Unlike typical service locator usage, however, we are only extracting configuration and a few shared dependencies.

Config injection takes this approach to avoid the service locator's God object and inherent coupling between usage and the God object.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.121.8