Dynamic database gateway on clojure

On our last project we started using concepts from clean arquitecture. Starting a new clojure project we tried to used several familiar concepts and integrate them on a web api we’re building. The gateway aims to abstract the persistence details from the application, and it’s a valuable concept. By using a gateway we are less likely to add datastore specific stuff to the application code, thus making it more independent.

Another advantage is that we can plugin several gateways. At this moment we have a mongodb gateway and we implemented a memory gateway to run our unit tests. Running a memory database drastically improves test feedback.

Memory gateway run:

Ran 35 tests containing 177 assertions.
0 failures, 0 errors.
Passed all tests
Finished at 10:30:49.205 (run time: 0.088s)

Mongo gateway run:

Ran 35 tests containing 177 assertions.
0 failures, 0 errors.
Passed all tests
Finished at 10:30:49.205 (run time: 2.345s)

We still have a small test library, but the difference is already visible. Two seconds is a lot of time when comparing to the instant feedback from the memory suite.

Gateway’s interface and usage

We implemented a function that chooses which gateway to use, based on an environment variable. The interface that all the code knows and uses is the following:

The gateway-call will try to find a save! function on a specific namespace with account-gateway on its name.

We build the complete function name, and dynamically resolve it to a concrete clojure function. After that we call it with the given arguments.

Sample implementation

This assumes that the function for the given provider is available and required. It will fail an assert otherwise. Here’s part of our mongodb implementation, using monger:

Our memory provider is similar, but stores information on an atom and operates on it.

Pros and cons

I believe that this approach makes our application more robust and reliable. Clearly separates logic from persistent details and allows a super fast test suite feedback with the memory provider, that boosts TDD. Allowing to inject other providers means that our CI can run all the test suite on all the supported providers, ensuring that everything works.

It does have some drawbacks. Having 2+ providers means that when we add/change something on the provider level, we need to update 2+ code bases. I believe that this will be far less frequent, but only time will tell.

@donbonifacio on twitter