Golabs IV,Dependency Injection in Go
Last updated: Oct 9, 2017
Introduction
This is the fourth edition of Golabs and a pretty important one where we explore Dependency Injection in Go.Understanding DI is very crucial to how you design your package/program to have hallmarks of clean code(Ease of testing, Low Coupling,High cohesion etc.)
We will extend on our Golabs-3 codebase which was itself written after iterations on Golabs-2 and Golabs-1 and end up with a sample code that will can be used as a point of reference for understanding Dependency Injection
Recap
In the previous Golabs, we learnt how to decouple a code’s dependencies and mock out those collaborators for testing.
But at the same time we saw that there was further scope for improvement:
- We expected the caller of our code to set the appropriate implementation of UserDao interface.If we observe closely we see that there are no compile time rules enforced which will prevent this line of code from not barfing on a nil reference if the caller did not call this method and set the appropriate implementation of the UserDao which is done in this line of the test code.
- In other words, the dependencies are implicit/assumed to be set.We all know what they say about assumptions yeah?
What is Dependency Injection?
On hearing ‘dependency injection’, the first connection that the minds (at least those coming from the JVM world ;) ) make is the collection of application context xml files, annotations like @Autowired @Components etc.Prolific use of Spring, the most popular framework in the JVM world has hardwired this connection.I actually understood the meaning of dependency injection while working on Google Guice.The Wiki’ Motivation Section has excellent starting material to understand why and what is dependency injection.I recommend everyone to go through it, even if it not Go specific.
Once we start understanding object graphs(how two or more objects are interconnected),we can begin to comprehend what dependency injection really is.Consider the following digram where the arrow head depicts the relationship Depends On

In an object graph, objects at higher levels typically are the ones that depend on the ones at the lower level.If the objects which are the dependencies change for some reason, the dependant objects should not change in accordance with the Single Responsibility Principle.
If the dependant objects instantiate dependencies themselves(Tight Coupling), this becomes inevitable.
To avoid this, it is recommended that the objects accept their dependencies prepared for themselves when they are being instantiated.
These dependencies should be expressed as interfaces and not implementations.
This is basic what and why of Dependency Injection.
Dependency Injection is simply put is the pattern where an object doesn't instantiate its dependencies on its own, but has them passed/wired up for it hidden behind a contract or an interface through constructors.
What Does Dependency Injection mean in Go?
The above definition applies universally and Go is no exception.In fact the lucid concept of interfaces and the method reciever
At a high level it means that the invocation point(the main package during actual execution and the test_XXX file during the testing phase) to your program has the responsibility of dependency resolution and bindings which can be passed around the other parts of your program.
To achieve this we create a struct named App (think of this as an Application Container) that holds all the necessary bindings and define the methods on this struct.
There is an addendum which involved proper code organization for proper execution in which I will cover in detail later on.But for brevity’s sake lets limit the scope to the testing phase only.Assume that such wiring and organization is already in place.
The Container struct looks like follows:
package api
// Container will hold the dependencies required for API
type Container struct {
Dao UserDao
}
We will make the SignUpUser() a method on this struct
func (c *Container) SignUpUser(w http.ResponseWriter, r *http.Request) {
//now accessing the implementation of the userDao is as simple as
reqBody := &UserSignUpRequest{}
parseRequestBody(r.Body, reqBody)
c.Dao.saveUser(reqBody)
}
We define a method on the Container to glue up the container to our controller layer as follows:
func (c *Container) InitRouter() *mux.Router {
r := mux.NewRouter()
r.HandleFunc("/users/new", c.SignUpUser).Methods("POST")
return r
}
The container is initialized with the mocked implementation in the test_api.go file that we have been using till now like this.As you can see this can be (and is) changed for every test execution
Our testing code becomes:
func TestUserSignup(t *testing.T) {
c := &Container{Dao: &MockedUserDao{}}
r := c.InitRouter()
server := httptest.NewServer(r)
for _, fixture := range signUpTests {
//execute & assert
}
}
You can checkout the code in its entirety after this changes here
What have we achieved?
By implementing dependency injection we have been able to get rid of the annoying gremlins in our previous implementations. We leveraged the statically typed nature of Go and took away the concern of the caller wiring up the dependencies for the callee.
We have made the dependencies explicit which makes the code easier to be reasoned about.
By designing the code in this manner we have made it easy to swap out the implementation of UserDao in our Container struct to take in an actual implementation talking to the datastore/Database during production with only a single line of change where we intialize the Container struct.All the other components have no reason to change if we were to(and have to) do this.
This is what is called as High Cohesion Loose Coupling
Most importantly the dependency injection is seen in practice and not seen as some auto-magic that the framework provides.
Conclusion
A code with high cohesion and loose coupling is easily extensible and maintainable.Dependency Injection forms the cornerstone of this thesis.
During the course of Golabs-I to Golabs-IV we have worked backwards to a goal of writing a easily testable, loosely coupled code from the point of Test Driven Development.
This discourse could be looked at as an iterative refactoring guide (Golabs I-IV)
OR
a starting point to write the code with all these hallmarks(Golabs-IV).
In the upcoming Golabs, we would look at implementing a simple production-ready Web App demonstrating all that we have learnt till now.