Learn how a Service Locator can be modeled in Clojure, why it sometimes works for plugin-style registries, and why explicit dependency injection is usually clearer for ordinary application design.
Service Locator pattern: A pattern where code retrieves dependencies from a central registry instead of receiving them explicitly from the caller.
Service Locator can be implemented easily in Clojure, but that does not mean it should be a default design choice. It is often convenient for plugin registries, runtime lookups, or integration hubs. For everyday application code, explicit dependency injection is usually clearer because it makes dependencies visible instead of hidden behind global lookup.
1(def services (atom {}))
2
3(defn register-service! [k service]
4 (swap! services assoc k service))
5
6(defn get-service [k]
7 (or (get @services k)
8 (throw (ex-info "unknown service" {:service k}))))
That is enough to demonstrate the pattern:
Service Locator feels attractive because it can:
Those are real benefits in some environments, especially when the set of implementations is dynamic.
The pattern also hides important design information. A function can look simple while depending on several services that are only discoverable at runtime. That hurts:
In ordinary application code, explicit injection is usually better:
1(defn send-order-email [mailer order]
2 (mailer order))
The dependency is visible in the function signature. That usually makes the design more honest.
Service Locator can be appropriate when:
In those cases, the registry itself is part of the problem domain of the infrastructure layer, not just a shortcut for avoiding explicit design.
flowchart LR
A["Caller"] --> B["Service locator"]
B --> C["Service A"]
B --> D["Service B"]
B --> E["Service C"]
The risk is not centralization alone. The risk is that the caller’s real dependencies disappear from plain sight.
Service Locator goes wrong when:
If most business code reaches into a central registry, the codebase usually becomes harder to reason about over time.
Treat Service Locator as an infrastructure pattern, not a default application pattern. Use it for registries and dynamic lookup boundaries when that indirection is real. Prefer explicit dependency passing for ordinary business logic.