Fork me on GitHub
#polylith
<
2023-08-19
>
greg04:08:48

The idea of creating interfaces in front of components feels like having interfaces to classes in OOP. In OOP interfaces tends to be overused very often introducing only unnecessary indirection. Here in polylith it feels a bit like that. While, maybe some people find it useful, in majority of cases, that indirection is never leveraged, because it is never needed. Most of the components never live to see a twin brother implementing same interface. At the same time, supporting indirection via these interfaces seems to be the major reason why bases are separated from projects in polylith design. I'm not against modularisation. In fact, I've build some projects around modularisation in a non-Clojure environment and I think it is really important to provide some sort of module-based framework for organising a project. Without that is impossible to keep big projects manageable over time, but the indirection does not seem to a key factor. Why am I wrong here and it is crucial to keep that indirection put on by default?

tengstrand05:08:39

Yes, this is the impression you often get when you start using Polylith, but after some time you realise that it's quite convenient to know where the "entrance" for each component is, and that it allows you to create cleaner interfaces for the users of the components (the developers). The interfaces are implicitly defined in Polylith within each component, and you don't have to define them elsewhere or with different names, as in e.g. Java where an interface and a class can't share name. In Polylith they can, and the interface is implicitly defined, just by having a namespace with the name interface (by default). The indirection is just a default, and you are free to put all your code in the interface if you like.

seancorfield21:08:23

When we're sketching out new code, we often just write the code in the interface and then refactor to a separate impl. We have some "interface" files that we never refactored - but in general it's nice to have the interface be just the functions in alpha order without the "clutter" of their implementation and all the ns dependencies. Like many things with Polylith, it makes a lot more sense when you've used it for a while:grin:

polylith 2
jasonjckn04:08:23

(disclaimer: i've not used polylith until 3 seconds ago) A middle ground I like to employ is, the following directory structure

componentname.clj
componentname/impl1.clj
componentname/impl2.clj
If the code is small enough to fit into a single source file, then I never actually create the subdirectory, I just use defn and defn-, but as it grows, organically comes a point where I create that subdirectory, and then do the 'interface.clj' like setup but inside componentname.clj. I find this a more evolutionary/natural approach. I also, use macro magic to alias vars from the interface namespace to the implementation namespace
(var-implemented-by-var!
  (defn my-if-name
    "docs"
    [db-conn])
  #'com.foo.componentname.impl/impl-for-my-if-name)
Keep in mind i'm not actually writing an implementation of my-if-name in the interface file, rather i just used 'defn' to satisfy the various 'intellisense'/lsp servers.

jasonjckn04:08:10

^ Is this setup even possible, I just saw :interface-ns "interface" in workspace.edn

seancorfield16:08:44

You have to use a distinct ns suffix so the poly tool can track usage.

👍 2