polylith 2024-05-29

Alexander Kouznetsov 2024-05-29T22:59:30.434269Z

Am I correct that any protocol that is a part of a public API for a component, should go into an interface.clj (as shown in v1)? Or should an interface only contain functions from the protocol and instead call the implementation protocol in their bodies only (v2)? interface.clj v1:

(ns my-component.interface)

(defprotocol Foo
  (do-something [foo args]))
interface.clj v2:
(ns my-component.interface
  (:refer [my-component.core :as core])

(defn do-something [foo args]
  (core/do-something foo args))

In my experience it can also reduce development friction to put protocols into separate namespaces. If devs don't always do form-by-form evaluation, and often reload entire namespaces, redefining a protocol will create a new interface and you'll need to reload implementations too for them to pick it up.

Alexander Kouznetsov 2024-05-30T18:14:04.183179Z

> So your main interface can’t define them since it requires the impl -- and the impl already requires the protocols. Oh, so otherwise there is a circular dependency issue? > development friction to put protocols into separate namespaces Oh, that’s a really annoying problem. I was thinking about using potemkin’s defprotocol+ to avoid that.

I think the general recommendation is to keep protocols separate in their own namespace, even outside Polylith? (for such reasons -- and I think adding potemkin would be a worse solution than "doing the right thing" 🙂 )

Alexander Kouznetsov 2024-05-30T18:33:37.634949Z

I’ve never came across this recommendation, is there any place it is published?

No idea. It's something I've seen mentioned several times over the years in various places, including here.

✔️ 1
🙌 1

The pattern we use is <top-ns>.component.interface.protocols for the ns and put protocols in there, which makes it easier for everything to :require them.

That way <top-ns>.component.interface is going to have what you show in v2, and then that impl ns will require the interface.protocols and then provide implementations.

So the protocol is still part of the public API (in an "interface" ns in the component) but only code that actually wants to extend that protocol needs to require the specific interface.protocols ns). Does that make sense @alexander.minolta?

Alexander Kouznetsov 2024-05-30T02:08:22.604359Z

Thanks for sharing! I’m curious why we put it separately? And also if an interface uses multiple interfaces, would all of them be put in a single protocols namespace?

Because the impl needs the protocols, in order to define fns that implement them, and maybe other code needs them to extend the protocols. So your main interface can't define them since it requires the impl -- and the impl already requires the protocols.