I’m going to add a bit of a cautionary tale here…
When we got started with Clojure at work – about a decade ago – we didn’t know about things like Mount or Component (they may not even have existed back then?) so we took the “easy” approach with things like database connections and cache setup and various other things that needed some “state” set up as our programs were initialized: we just put them in global variables! Sometimes we used a def
with a computed value right there inline, sometimes we used a delay
so it was “initialized” on first use, sometimes we used an atom
and computed the value explicitly at program startup and then reset!
it to that value. It all worked. We just referenced those top-level Vars as needed in other namespaces and we continued building.
Over time, we had more programs that all needed some combination of these global resources and we started to write code to manage their initialization in more comprehensive ways, but every new app we built had to remember to call that initialization code in the right order: we had some code that needed both the database stuff and the caching stuff, then we moved all the database connection metadata to config files, now the database connection initialization depended on the environment configuration initialization. And we fairly quickly had a mess of dependencies that all needed to be initialized in a certain order and passed into each other in certain ways. Plus we had a lot of code that all depended on these globals.
We also had all of this setup in our test code, sometimes with different pieces needing different values – and with all those globals, it was often much harder to test code because you couldn’t just mock a resource.
We needed a better approach.
This was exactly what Stuart Sierra’s Component library was designed for: you declare a “lifecycle” for each resource – a pair of start
/ stop
functions – and you declare the dependencies each resource needs prior to startup. Component figures out the right order to initialize everything and how to build your “system” from all these inter-dependent pieces. And then, instead of global vars, you pass the system (or a subcomponent of it) into each function that needs it, starting right at the top of the call chain in your -main
function.
Component is simple, and if you use it from day one it is also easy. Your tests can very easily swap out real resources for mock resources as needed, and can also very easily build multiple systems so you can test interactions (this has been very important for us as it makes testing cluster synchronization code so much easier when we can run multiple “systems” in a single JVM for testing!).
We build all new code this way and we’re very happy with Component.
The cautionary tale? We had a pretty large codebase before we introduced Component and we had a lot of code that depended on those global Vars! We introduced Component several years ago and we still have a few pieces of our system that rely on globals instead of a (sub)component being passed through the call chain. Not adopting Component early on has been very, very painful. If I could go back in time and give one piece of advice to my younger Clojurian self, it would be “Use Component! Now!” and I could have avoided so much pain as our applications grew.
Dorab gave an excellent answer about what your options are and why you might use them but Phil is also right insofar as you won’t feel you need them when you get started and they could be just another obstacle to learning Clojure – but my advice, from a decade of production Clojure, is: use Component as soon as you build your first production app. Use Component for your database connection pools (next.jdbc
supports this out of the box!), use Component to start (and stop) your web server, use it for anything that has an initialization that depends on anything else. It will make it easier to add new resources to your app, it will make it easier to test your app.
As for Mount, some people love it (I don’t – see this thread about DI in Clojure). Some people love Integrant for this (I don’t – see that same thread!). That’s a good thread to read as a whole for more background on dependency injection (which is really what this whole Component/Mount/Integrant thing is about). There’s also this short thread about Mount and Component – plus several other threads here where either, both, and/or Integrant are discussed so it’s worth using the search feature at the top of the page to see what other discussions have happened here about the pros and cons of each.