There are many out there who seem stumped by Dependency Inversion Principle (DIP) in go, especially if they don't have experience of it in other languages it can seem a lot of work for little payoff. Those that do persevere often, like myself, immediately go off and look for a third party Dependency Injection (DI) package, but it turns out that it is relatively easy to implement DIP, via Inversion of Control (IoC), and can be achieved using the built in functions of go.
First things first, the functions required to implement IoC are in the language, but a simple description of how to do it is hard to find.
What is IoC?
At it's simplest level the basic concept of IoC is that any code that needs an object does not create it, rather the objects are already available and an implementation can be reused, and in go this can be implemented using factory functions defined on startup and stored in a map. Using this method the actual binding is not done by a function, function uses the factory to get the object, usually reference by it's interface.
So what does this achieve? Well at the simplest level the functions are not directly coupled to an object, and so other objects, based on the same interface, can substitute in and used. This comes into it's own in two particular scenarios, unit testing and ability to plug different providers into the application.
In unit testing, we are able to create mocked/fake objects to ensure that the unit tests are only testing a unit of code, and not the infrastructure beneath it.
In the plug-in case, this enables us to change systems such as databases, and message queues, relatively easily, and in a way that means that the original providers can be switched back in without reverting too much code.
Example
The example below is based on a imaginary car sales system. The idea is that when ordering a car the request must go to a car factory that builds that particular model.
The code is broken down into it's four key areas, these being the interfaces, the concrete implementations, the 'class' factories (I know classes don't actually exist in go), and a method that defines the mapping of the objects and later uses those objects to get details of the car that has been 'built'.
If you are new to DIP you might struggle to understand how this benefits development, but if you study the code closely, and particular the main function, it should become obvious that if you wanted to substitute the make and model of car, you can register a new factory and use pass the key for this factory into the 'class' factory instead of the existing "Ford" key.
Loading ....
Summary
In summary, the simple IoC example above does provide a very straight forward option to DIP, but it isn't perfect. In the ideal world we want to setup object mappings at runtime and not build time, which is what we have here, is it possible? Very much so, and in future posts I will look to take the example and update it to be driven from a configuration file.
Would I recommend this as a path to implementing SOLID principles? I think if done properly using this approach would work well, especially on smaller projects.
<< Previous: Introduction
Next: go-inject >>
Links
Dependency Inversion in Go - Introduction
Dependency Inversion in Go - Simple IoC
Dependency Inversion in Go - go-inject
Dependency Inversion in Go - go.inject
Dependency Inversion in Go - depinject
Dependency Inversion in Go - Summary