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 app.views
.
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.
:as-alias
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 def
s, 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.