SQL persistence with Clojure
30.06.2014 PermalinkTL;DR I released aggregate, a library that handles load, save and delete for nested data structures from/to SQL tables.
About 1 year ago I made myself familiar with the Clojure libraries for accessing relational DB systems using SQL. My goal was to compile a short introduction for one of the meetups of the Clojure User Group Bonn. By that time, SQLKorma seemed to be the most advanced option, offering a quick setup, a nice looking DSL that helps to even specify foreign key relations and allowed to create and compose SQL queries. And so I based some of my examples on Korma, we had an interesting evening, and I stopped further investigation.
By chance, I recently came across Yesql, which allows you to use existing SQL statements by importing them as functions into a namespace. While libraries like HoneySQL or SQLingvo are fine to create composable queries from Clojure data, Yesql removes any indirection between an internal Clojure-based SQL DSL and SQL itself. After all, SQL is already a DSL, and an additional layer adds complexity that you face whenever you ask yourself "Here is the SQL that I need, now what do I have to write down in Clojure to get it?". As always, it's about trade-offs. With Yesql, you lose composability on the query level and any DB-dialect independence. But you can develop your SQL straightaway with your favorite DB tool and be done.
I really like the deconstructive design approach that most Clojure developers prefer. Ring and friends are a great example, and I'd like to see libraries in that spirit also for SQL persistence. So, we have clojure.java.jdbc for very basic DB access, on top of which we have SQL producing/using libraries like HoneySQL, SQLingvo and Yesql. They have a very focussed task: make SQL available to use with clojure.java.jdbc. Furthermore there are libraries that help manage schema creation and evolution like Lobos or Ragtime.
Still missing in that picture are functions to handle more complex, nested data. Something like maps with vectors of maps, containing other maps and so on. We're not in OO, but there is still an impedance mismatch between nested data and its relational representation based on tables and foreign keys. Korma takes relations into account when loading data, but I wasn't able to find a relation aware save and delete functionality. In addition it's more "batteries included" than I prefer, and not in the same way focussed and composable with Yesql and the like.
So I created aggregate to solely bridge the impedance mismatch between nested data and the relational model. It is agnostic to concrete SQL, and it can leverage functions from SQL oriented libraries to handle even more complex data mappings.
I released it on Clojars. Any feedback is welcome.