The code for this project in this article can be found here

Let me ask you one question: What important would you place on things “just working”?

I have long been a fan of dependency injection and inversion of control in general, and if you’ve read my previous articles, you’ll know how big of a proponent I am of it’s use in NestJS. It’s so nice to be able to inject a service into a controller, a logger into a module, or a DB handler into a repository and it “just works” - no need to worry about how. I just want to write business logic, and it’s all just there, ready to be used and injected in by the DI container.

Because of this, I began searching around to see if Go had any DI tooling or libraries to give a similar experience. That’s when I stumbled upon fx, a library created by Uber to provide a full dependency injection framework for Go, and apparently it’s the backbone being almost all of Uber’s Go services (which I assume is a lot given their style guide is the industry defacto). After some time playing around with it, I’ve come out with one persistent observation:

FX makes writing Go programs feel like magic Link to heading

To understand how, we’ll build a very simple document database engine, and we’ll use FX to make managing the dependencies a breeze.

The Project Link to heading

Our simple document database will truly be simple - we’ll he using the filesystem and JSON documents store documents to collections (🍃), and use a simple query syntax to find and list those documents, with queries like:

# Create a new collection called "users"
CREATE users
# List all the collections
LIST collections
# Find all documents in the "users" collection
GET users
# Find all documents in the "users" collection with a "name" property equal to "John"
GET users WHERE name = "John"
# Delete all documents in the "users" collection with a "name" property equal to "John"
DELETE users WHERE name = "John"

To do this, we’ll need a few different components:

  • A query parsing engine (to parse the query syntax)
  • A collection engine (to setup and manage collections)
  • A document engine (to read and write documents within collections)
  • A storage engine (to read and write documents to disk)
  • A logger (to log things, obviously)
  • A webserver (to expose the query endpoint, for simplicity we’ll just use a one-endpoint webserver)

The benefits of the DI system will become apparent if we look at the dependency graph:

graph QueryParser --> CollectionEngine QueryParser --> DocumentEngine QueryParser --> Logger CollectionEngine --> StorageEngine CollectionEngine --> Logger CollectionEngine --> DocumentEngine DocumentEngine --> StorageEngine DocumentEngine --> Logger StorageEngine --> Logger Webserver --> QueryParser Webserver --> Logger

As you can see, there are a good amount of intermingled dependencies here, and it’s not immediately obvious how to get them all into the right places. Imagine you had a real database engine like WiredTiger, with transaction engines, paging systems, and a whole host of other dependencies to manage!

This is where FX comes in.

The Code Link to heading

This section is still in progress, but you can take a look at the current codebase here