A Quick Note on the Clojure (Backend) Web Space
(Last Updated on 16 Sep 2017. Thanks nha, gsnewmark and danielcompton for corrections and discussions!)
(Note to Experts: if you find any factual error in this note, please tell me by leaving a comment in the comment section below. Many thanks!)
Frameworks
Clojure as a community has emphasized two points (among others):
- No complicated, Rube-Goldberg-Machine framework - prefer simple, focused, composable libraries for specific features. Pick your own choice for each feature and combine them in your application.
- Data is a first-class-citizen. Instead of coupling it with function, let it stand alone and have functions operating on/transforming them.
That being said it is tedious and intimidating for a rookie to have to make informed choice on every step. So there is still some “frameworks”:
- Luminus is a funky thing: while it is called a “micro framework” on its webpage, if you look closer you will find nothing beyond the series of guide webpages. It is not even a library! Instead it is just a lein template you can call to scaffold a web project with sensible default choices of libraries (per point 1 above) and all the tedious configuration done for you.
- Arachne (still in alpha) is more opinionated and co-opt point 2 above: the app is data-driven and the kind of messy configurations so common in other web framework (in other languages) are “real data” and given first-class, systematic/unified treatment. (Similar project and/or successor on this line is the Duct framework)
- Liberator is more a library targeted to those who want to write a RESTful web service backend. It has put heavy emphasis on fully embracing the doctrine of REST - even those that are less adhered to in practise (HATEOAS comes to mind). It seems that its more pragmatic, asynchronous counterpart - Pedestal - has more adoption in the real world.
Build Tools
Just in case you don’t know (yet), there are two:
- Leiningen (lein in short) is the standard one and is a task runner, declarative project configuration, and dependency management combined (sort of like Maven if you’re in Java, npm if you’re from Javascript, rake if you’re in Ruby I think(?), XX if you’re from language YY - really most modern languages nowadays have this particular combination as the default)
- Boot is (ironically) the new kid on the block and pursue a procedural style that allows more flexibility/programmability. (E.g. Just Ant for Java, or Gulp over Grunt for Javascript guy - I know, that two are ancient in 2017, but still)
Servers
(I assume you have the Java background to understand, well, the Java part. If not, well that’s too bad - wait until my planned next post where I will briefly talk about that in a tangential subsection :P )
Of course two “universal” deployment options are:
- Build an uberjar that is completely standalone (web application is packaged with an embedded web server) and then just execute it on the JVM
- Build an (Servlet-compliant) uberwar and deploy it into any Java web server / web container
Unfortunately thing starts to get unruly at this point, so tighten your seat belt…
While Servlets are a good thing in spite of how it might be badly designed, by abstracting away differences between web/application servers and presenting a uniform interface to application developer, maintaining this kind of standard (especially in Java) pretty much ensure that it will be slow to pick up on innovation in the web server space. But the rise of asynchronous web servers such as Node.js and other emerging forms of interaction (such as websockets) put on pressure for adoption. When the standard fails to adapt fast enough (Newer version of the Servlet spec has support for them), people will begin to work around (and hence without) it. So instead of a unified interface, expects custom API specific to each web server.
Another trend that may be unfamiliar to people used to working in an enterprise context is the rise of embedded server. 2 It is interesting to note a kind of Inversion of Control here: instead of deploying an application to the container/server and letting the server’s execution manage starting/stopping the app, we now have explicit control: the application contains the server and triggers the start/stop of the server through application logic. 3 One advantage of this approach is that the implicit logic of a server managing a deployment is now explicit so that when problem occurs, things are more traceable.
So, with these understanding, what clojure-specific web servers do we have?
First, ring is the Clojure analogue of the Servlet spec. It is a HTTP server abstraction/interface plus a number of small libraries.
- http-kit is a ring-compatible web server written in Java and Clojure.
- Jetty and Netty are classical Java web servers. Jetty is a standard, embedded Java server, while Netty is closer to Node.js with emphasis on the Transport Layer by providing access to TCP/UDP/socket server as well as supporting asynchronity.
- Ring itself has an adaptor for Jetty, and that’s an (easy) default choice in some contexts.
- Aleph is a server that’s a thin wrapper over Netty. It can also acts as drop-in replacement for any ring-compliant server.
- Immutant is a more heavyweight library/server that includes other services needed for more sophisticated system (messaging queue for instance). It can be deployed to either Java’s WildFly or since 2.x the Undertow web server.
Libraries
There are just too many of them to discuss here. Instead I just highlight some of the foundational ones:
- For something similar to dependency injection in Java, we’ve got application lifecycle/state management libraries: Component vs Mount.
- For the routing part of your typical/classical MVC framework, we’ve got Compojure vs just plain ring (Remember ring is an API + a collection of libraries) vs bidi.
And One More Thing…
I haven’t mentioned database in this post so far. Well of course you can use any as long as JDBC supports it… database is not usually tied to a particular programming language. But since we’re talking about clojure, it is criminal not to mention Datomic, a super-innovative “database” made by Cognitect (if it can be called that, per the talks by Rick on the Database-as-a-value). Unfortunately, it is one of the rare infrastructural piece of software in our ecosystem that is not Open Source nor free (they have a free version, but it has restrictions).
References
Thanks Google! (and the people in the Clojure community ;) )
- Announcing Aleph
- Advancing Duct
- My first time using boot over leiningen
- Differences between Jetty vs Netty
- Discussion on Clojure web servers on stackoverflow
-
See their wiki for their own assesement/comparison of yada vs Liberator and Pedestal. Also see their blog post for a sales pitch. ↩
-
My own pet theory: Ever heard of DevOps? Instead of externalising config and dependent systems with a dedicated, separate team to manage them, why not put everything back to the hands of developer with the same powerful set of tool - general purpose programming language - to manage config? Really nice for lone wolf developer… the operation department, not so much. ↩
-
I will confess to having personal prejudice that embedded server are somehow slower: no they are not a priori slow. How could it be? Afterall web/application server are just codes, and where exactly are those code executed does not affect its performance. ↩