Dependency Injection in Go using Fx
Tags: go, di, scalability
Published: 2020-04-05 07:42:19

---
title: Dependency Injection in Go using Fx
published: true
description: A dependency injection framework for large-scale Go projects
tags: golang, go, di, scalability
series: Dependency Injection in Go using Fx
cover_image: https://i1.wp.com/pmihaylov.com/wp-content/uploads/2020/04/wires-209767_1920.jpg?w=1352
canonical_url: https://pmihaylov.com/dependency-injection-go-fx
---
<!-- wp:paragraph -->
<p>When you initially start a Go project, your main function typically has a bunch of wiring code - initialising your routes, plugging in middlewares, initialising your template engines, loggers, etc.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>This is one of the great things about Go - you don't have any magic happening behind the scenes. The code is all there and you can read it and debug it.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>But as your software grows, you start feeling the growing pains - your main function starts becoming more and more convoluted. You start having all sorts of small bits and pieces plugged in here and there - healthchecks, database setup code, metrics, tracers, external API connections, etc etc.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>And what if your application grows into a microservice architecture? What do you do when you have five different microservices demanding the same bunch of setup code, specific to your environment?</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In this article, I will introduce you to <a href="https://github.com/uber-go/fx">Fx</a>. It's a Go framework which solves both problems outlined above using dependency injection.</p>
<p>Let's jump in.</p>
<p>All the code from this article is available on <a href="https://github.com/preslavmihaylov/fxappexample">github.com/preslavmihaylov/fxappexample</a>.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Creating a Simple App Using Manual Wiring</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>What I described in the introduction - having your setup code visible in your codebase (often times the main function) is called manual wiring.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Let's see an example to understand how it looks like in practice.</p>
<p>If you already know what manual wiring is then simply skip to the next section. But make sure to review the code at the end of the section to have enough context for what we're building.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The typical way you start a new web app is via simple hello world server:</p>
<!-- /wp:paragraph -->
<p>{% gist https://gist.github.com/preslavmihaylov/67fce7b32d7b6fed86019774f1dfba7c.js %}</p>
<!-- wp:paragraph -->
<p>And here is the main function:</p>
<!-- /wp:paragraph -->
<p>{% gist https://gist.github.com/preslavmihaylov/d3f930801d3850fed8c1eaeeafddefaf %}</p>
<p>Nothing fancy so far.</p>
<p>At some point, you also need a logger. Adding it to your main using the logger from <a href="https://github.com/uber-go/zap">uber-go/zap</a>:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/a9224ffd160567d0fafb4aaa2e661886.js %}</p>
<p>That logger is then assigned to a field in the http handler. That part of the code I will omit for brevity.</p>
<p>But look at that hardcoded address. Let's export it into a configuration using <a href="https://github.com/go-yaml/yaml">go-yaml/yaml.</a></p>
<p>Our configuration will look like this in <code>config/base.yaml</code>:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/90f26c9e9303cf627490464503fdeeca.js %}</p>
<p>In order to parse it, we'll have to create some more structs:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/444ea031a0193247309a38fe4cfee238.js %}</p>
<p>And later use them to un-marshal the yaml and pass the address to our server:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/07f4eff9d893a0f71d0afdf487603698.js %}</p>
<p>Let's stop here. I think you get the idea. We construct each structure ourselves and manually wire the dependency to the component who needs it.</p>
<p>All the code from this section is available on branch <a href="https://github.com/preslavmihaylov/fxappexample/tree/v1-manual-wiring">v1-manual-wiring</a>.</p>
<h2>Limitations of The Current Approach</h2>
<p>To be honest, this approach works quite well for small to medium apps. The benefit is that the code is easy to trace, understand, debug. There is no hidden magic.</p>
<p>The problem, however, is that as the application grows, there will be tons of other components created which you will need to manually wire in your main function as we've been doing so far.</p>
<p>As time passes, the manual wiring will become more and more complex, easy to get wrong and hard to manage.</p>
<p>This is what dependency injection frameworks are for - they allow you to not bother with this wiring logic as they do it for you.</p>
<p>This allows your application to scale gracefully as more and more components are added into it.</p>
<p>The second big problem with the approach so far is that reuse of common components is harder.</p>
<p>In case you are working on a microservice architecture, there will probably be a lot of infrastructure-related components which stay the same between services. They are not directly related to the service's domain.</p>
<p>Examples are emitting metrics, logging, standard configurations, health checks, etc.</p>
<p>If you rely on manual wiring, you will have to either copy-paste this boilerplate across services each time you bootstrap one or, best-case scenario, export the common components into separate packages to reuse.</p>
<p>But in the latter case, you will still have to deal with instance initialisation and wiring yourself.</p>
<p>So if you've grown to a scale where you are facing these problems, what options do you have?</p>
<h2>Scaling the Application using Fx</h2>
<p><a href="https://github.com/uber-go/fx">Fx</a> is a <strong>dependency injection application framework</strong>.</p>
<p>That's a mouthful. What does it mean?</p>
<p>The "dependency injection" part means that it can take care of the component wiring code for you.</p>
<p>The "application framework" part means that this is not simply a library you plug in and use where needed. Instead, it is a framework, which manages the entire lifecycle of your application - imagine ASP.NET for C# and Spring for Java.</p>
<p>Those are application frameworks.</p>
<p>But although I am comparing Fx to those behemoths, it is far simpler than those frameworks as you'll see.</p>
<h3>But What is a Dependency Injection Framework?</h3>
<p>First, let's <a href="https://pmihaylov.com/learning-golang-experience/">learn</a> what a dependency injection framework does for a living. At its core it simply connects "providers" to "receivers".</p>
<p>A provider is some component which says "Hey, here is my instance. Feel free to give it to anyone who needs it."</p>
<p>A receiver is some component which says "Hey, in order to work properly, I need instances of components X, Y and Z".</p>
<p>Those X, Y, and Zs are "dependencies" the receiver needs in order to work.</p>
<p>Note that receivers can also be providers.</p>
<p>So what the DI framework does for you is it automatically connects providers to receivers. In case some receiver cannot get one of his dependencies, the framework throws an error.</p>
<p>That's all you need to know to get started. Now let's refactor our application to use the Fx framework.</p>
<h2>Converting our Service into an Fx Application</h2>
<p>The first step is to install the library via <code>go get go.uber.org/fx</code>. Next up, let's boot it up in our main:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/89ab28eb80517706c0f54893f769d323.js %}</p>
<p>If you keep everything as-is and run your application now, you will have a fully-functioning empty Fx application.</p>
<p>Now, let's start refactoring the rest of our code as well.</p>
<p>To keep things simple, all following changes for this section will be done in <code>main.go</code>.</p>
<h3>Extracting the basic providers</h3>
<p>We'll start with the configuration:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/7399b1fc50bd2395d266fd423ad21106.js %}</p>
<p>What we just did is create a provider function for our configuration. We now have to declare to Fx that this provider is available for use:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/430f0e9d2e79d09237877a14d0fa1ecb.js %}</p>
<p>Let's now do the same for the logger:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/b9513b6bc2ffee94a1c9ad68092affb1.js %}</p>
<p>Here, we did the same refactor as the one for the configuration. The only difference with the original implementation is that we removed <code>defer logger.Sync()</code>.</p>
<p>We'll add that later in an application shutdown hook as it is meant to be executed when the application exits.</p>
<p>One additional dependency we have to provide is the standard http serve mux. It is a dependency to our http handler:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/44433a4bb46d9a2cb047c3c67d9c9388.js %}</p>
<p>You could wrap that also in a provider function. This would allow you to plug in any additional standard options into the serve mux, which your application might need.</p>
<p>At the moment, we don't need that so I've provided the function from the http package directly.</p>
<h3>Extracting the http handler - our first receiver</h3>
<p>Finally, let's provide our http handler's <code>New</code> function and delete the rest of our main function:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/2e1590e23a6997cdea2daae66092b106.js %}</p>
<p>This last addition is an example of both a <strong>provider and a receiver</strong>. Why? Let's recall the http handler New function again:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/45bf2dee2b53e534ea0346d1619794e0.js %}</p>
<p>Notice the parameters to the function. All other functions we added so far didn't have any parameters. That meant that they only provided some instances, without having any additional dependencies - they were only providers.</p>
<p>This function is now both a provider and a receiver. It is a provider because it returns a new instance of our http handler.</p>
<p>But more interestingly, it is a receiver also as it expects a logger and a <code>http.ServeMux</code> to work properly.</p>
<p>Where do those parameters come from then? They come from the rest of the provider functions which we created earlier - <code>ProvideLogger</code> and <code>http.NewServeMux</code>. </p>
<p>Fx does the heavy lifting of correctly routing the instances to where they are needed.</p>
<h3>Registering the lifecycle hooks</h3>
<p>One final touch we have to add is adding an <code>OnStart</code> hook. We need that in order to finally boot up our server after everything else is setup. <br />We'll also need an <code>OnStop</code> hook in order to flush the logger's buffer on program exit:</p>
<p>{% gist https://gist.github.com/preslavmihaylov/02bdd10e6bbee1b1df0d6b59b44f753b.js %}</p>
<p>Notice that this is also a receiver function as it depends on <code>fx.Lifecycle</code>, <code>*zap.SugaredLogger</code>, <code>*Config</code> and <code>*httphandler.Handler</code>.</p>
<p>And there you have it! We are now running a full-fledged Fx application.</p>
<p>Take a look at the entire code from this section on branch <a href="https://github.com/preslavmihaylov/fxappexample/tree/v2-fx-example">v2-fx-example</a>.</p>
<h2>Conclusion</h2>
<p>Some might look at the initial version of the code and think that it is better as it's much simpler and there is no magic going on.</p>
<p>And that is definitely true. This is why I suggest using Fx only if you really need it. For small to medium size Go applications, sticking to manual wiring makes sense.</p>
<p>After all, every tool we use should be able <strong>pull its weight</strong> - it's benefits should outweigh the costs. In this case, the cost is in terms of complexity and readability.</p>
<p>Additionally, since this tool relies on reflection, your IDE won't be as helpful in finding out where a dependency comes from. Hence, your code will be harder to trace &amp; debug.</p>
<p>However, as I stated in the beginning of the article, your manual wiring code will start becoming more and more complex. That is the point when considering a framework like this one makes sense.</p>
<p>And additionally, if you're maintaining several services which rely on common components &amp; configuration, Fx can then help as well by allowing you to separate your application into reusable modules.</p>
<p>That is something we are going to explore in the <a href="https://pmihaylov.com/shared-components-go-microservices/">follow-up article</a> as this one is already becoming too large.</p>
<p>Stay tuned.</p>