
Dependency Injection in Go using Fx
This article is part of the series GoLang: Building a Web Server
As professionals, we should never stop learning. Learning is the one true way to ensure we stay in demand and continue delivering value to our customers. Doctors, lawyers, and scientists are all highly respected professionals and all focus on continuously learning. Why should programmers be different?
Hence, continuing where we left in the previous article. Once we have a basic HTTP server in GoLang up and running, the next step is to build the base for the project so that we can write more and more code easily to cater to varied use cases of the service.
What is dependency Injection?
A software design pattern that implements inversion of control for resolving dependencies is known as Dependency Injection(or DI hereafter). In dependency injection, a ‘dependency’ is an object that can be used (service) and ‘injection’ is the passing of a dependency to a dependent object or a client that would use it. Dependency Injection is a subset of Inversion of Control (IOC)…
The above-mentioned definition is something which you have read multiple times at multiple places, yet somehow it doesn’t stick. So here’s a simpler version of it:
Dependency injection is a pattern for object composition. A parent object provides all the dependencies required to the child object.
where:
- The parent is the object that instantiates and configures the child object it uses.
- The child is the component that is designed to be passively instantiated. I.e. it is designed to use whatever dependencies are provided by the parent and does not instantiate its own dependencies.
There are basically 3 types of Dependency Injection, namely:
- Constructor Injection
- Setter Injection
- Interface-based Injection
What is Fx?
According to official documentation published by Uber, Fx is an application framework for Go that:
- Makes dependency injection easy.
- Eliminates the need for global state and
func init()
.
Fx uses the constructor injection pattern, let’s try to understand how exactly it makes dependency injection easy in Go.
I strongly recommend you to go over the Fx documentation before reading further.
In order to keep things simple, we will try to convert the HTTP server from the previous article into an Fx App.
- The first step is to install the library via
go get go.uber.org/fx
. - Edit the
main.go
with the initializer of an Fx App. If you run this usinggo run main.go
you will have a basic Fx Server running with nothing to serve.
func main() {
fx.New().Run()
}
3. Now we will provide the HTTP serve Mux to the application by adding it to the New
. We need to provide this to the application context so that it can be injected into our HTTP handler.
func main() {
fx.New(
fx.Provide(http.NewServeMux),
).Run()
}
4. Update the HTTP handler code as per the Fx spec so it can be provided into the application context. The updated looks like this:
package httphandler
import "net/http"
// Handler for http requests
type Handler struct {
mux *http.ServeMux
}
// New http handler
func New(s *http.ServeMux) *Handler {
h := Handler{s}
h.registerRoutes()
return &h
}
// RegisterRoutes for all http endpoints
func (h *Handler) registerRoutes() {
h.mux.HandleFunc("/", h.HelloWorld)
}// HelloWorld handler which recieves the user request
func (h *Handler) HelloWorld(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("Hello World"))
}
Yes, you are right, nothing has changed. It’s completely the same as the code we wrote for our manually wired server, and that’s the beauty of Fx.
5. The last and final step is to start our listener, for this we will leverage the Fx Lifecycle Hooks. We will go ahead and update the main.go
file to as below:
func main() {
fx.New(
fx.Provide(http.NewServeMux),
fx.Invoke(server.New),
fx.Invoke(registerHooks),
).Run()
}
func registerHooks(
lifecycle fx.Lifecycle, mux *http.ServeMux,
) {
lifecycle.Append(
fx.Hook{
OnStart: func(ctx context.Context) error {
go http.ListenAndServe(":8080", mux)
return nil
},
},
)
}
We are all set to run the application which will serve the “Hello World” response on “/” route.
go run main.go
Please find the code here on Github
I do understand that this might not look like a huge achievement from what we did in the manually wired server, and might be confusing at the first glance. How and where are things getting instantiated? What on earth are hooks? What exactly does the fx.New(something).Run()
do? …..and many more questions.
But, trust me as we move forward with our web server development series you are going to realize what value does a good DI framework adds to your project. Although there are many upsides of DI, there are few downsides as well:
- Dependency injection creates clients that demand configuration details to be supplied by construction code. This can be difficult when obvious defaults are available.
- Dependency injection can make code difficult to trace (read) because it separates behavior from construction. This means developers must refer to more files to follow how a system performs.
- It requires more upfront development effort.
Now with dependency injection explored and basics of Fx covered. In the next article, we will try to learn the fundamentals of Fx Modules. We will also try to add these basic building blocks of a web server in our demo HTTP server and see how Fx Modules makes our life easy during the whole process.
Coding Fun Fact: The first programmer in the world was a woman. Her name was Ada Lovelace and she worked on an analytical engine back in the 1,800’s.
Request: Please add your valuable feedback in the comments, that would really help me improve the quality of content and bring it in-line with your expectations.