While working on stuff (of course in Clojure), I kept running into problems with namespaces having circular dependencies. For example I’d have an
app.router namespace that defines the, uh, routes. I’d have an
app.url namespace that contains helper functions to generate absolute URLs based on route data (so it depends on
app.router). I’d have an
app.views namespace that uses those URL helpers (thus depending on
app.url), and these views would be referenced in the routes so
app.router would require
This completes the dependency circle and is the beginning of my journey. There are a bunch of ways to deal with circular dependencies in Clojure, but I won’t go in-depth about all of them.
Protocols and multimethods
These are my go-to tools for breaking the dependency circle. Put the declaration of the protocol (
defprotocol) or multimethod (
defmulti) in a separate namespace that then others can reference. In the example above, I’d have the Link protocol in the
app.url namespace setting up an
href function, while in
app.router I’d have the implementation (
extend-protocol) that closes over the router, and in the
app.views I could use the protocol function without issue. It’d be the same with a multimethod too.
Especially in a re-frame frontend app, I often require namespaces just so I have a shorter alias to use with namespaced keywords. In newer Clojure versions it’s possible to use
:as-alias to only introduce the namespace alias without actually requiring the namespace. Just be sure that the aliased namespace is required “properly” somewhere or it won’t be loaded at all and any
reg-subs or similar re-frame registrations won’t happen.
Passing the functions around
In a functional language like Clojure, functions are things too that can be passed around just like strings or maps can be. So it’s possible to pass the dependencies around as well as a form of dependency injection. Personally I’m not a big fan of this, as it just doesn’t feel right to me, but that’s just me.