polylith

ag 2024-04-25T07:06:51.998689Z

I have a toddler's question: why is that in the documentation, in poly project and in realworld-example-app the workspace level :extra-deps are prefixed with poly/, and in user-manager-example they are like this:

:extra-deps {poly-usermanager/web {:local/root "bases/web"}
             poly-usermanager/app-state {:local/root "components/app-state"}
             ...
Is it because these are :local/root deps, and it doesn't really matter, and you can have them like foo/web, bar/app-state, etc.?

2024-04-28T05:58:39.815879Z

in our project, we use the interface name

tengstrand 2024-04-28T06:24:02.385669Z

Yes, that should work too, because interface names are unique per project.

👍 1
tengstrand 2024-04-25T07:42:28.734059Z

Yes, that's the reason (`poly/` is just a convention). As long as you don't share code outside the workspace, you can use any valid prefix.

👍 2
Vladimir Bokov 2024-04-25T09:46:39.493769Z

Regarding https://clojurians.slack.com/archives/C013B7MQHJQ/p1712118156257669?thread_ts=1712106688.405469&cid=C013B7MQHJQ I took a look into seancorfield/usermanager-example and noticed schema component https://github.com/seancorfield/usermanager-example/blob/polylith/components/schema/src/usermanager/schema/core.clj#L2C14-L2C34 usermanager.database.interface like you said above • schema doesn't specify the dependency on database in components/schema/deps.edn like you said ◦ imo it's unexpected, but let's assume it's ok • there's {:local/root "../../components/database"} in projects/usermanager/deps.edn ◦ but if I comment it out this single line, I'm still able to run clojure -M:dev -m usermanager.web.main and the app works ◦ building the app clojure -T:build uber fails • there's {:local/root "components/database"} in root deps.edn ◦ if I comment it out, app cannot start with clojure -M:dev -m usermanager.web.main regardless dependency stated in projects/usermanager/deps.edn TLDR: effectively for repl/dev only toplevel deps.edn matters, for the build - only projects/usermanager/deps.edn, do I miss anything @seancorfield? imo it's a bit error-prone and non-intuitive when you try to think about components as dependencies, the most expected behaviour (again imo) would be • either including :local/root only in components/schema/deps.edn and hook/patch into tools.deps or preprocess/generate projects/*/deps.edn and toplevel deps.edn • or force people to a total DI and pass all database functions as arguments into schema (looks too enterprisy tbh)

tengstrand 2024-04-25T10:43:52.940489Z

> • schema doesn't specify the dependency on database in components/schema/deps.edn like you said > ◦ imo it's unexpected, but let's assume it's ok Bricks (components and bases) only know about interfaces (not other bricks). That's why the concrete implementation of an interface (component) is specified by each project, so that the implementation of an interface can vary between projects.

Vladimir Bokov 2024-04-25T12:27:50.884559Z

ok, so this is kinda DI on namespace level? in comparison to DI on argument level when we could rely on actual definterface (including system's component DI being passed through)

tengstrand 2024-04-25T12:49:38.395059Z

On namespace level yes, but they are not injected. The bricks become available when we specify them in a project, in the same way we specify libraries (which are also not injected). I tend to see bricks (and libraries) as living side by side. I try to explain this in the Composable section in https://medium.com/@joakimtengstrand/understanding-polylith-through-the-lens-of-hexagonal-architecture-8e7c8757dab1 blog post.

Vladimir Bokov 2024-04-25T13:14:14.301679Z

Here we illustrate how bricks are used across projectsthanks for the article, however interestingly I noticed a "development" project after this line. does it mean there should be no toplevel deps.edn at all (but projects/development/deps.edn instead)?

tengstrand 2024-04-25T13:17:08.029569Z

The development project is configured in ./deps.edn and not under projects as other projects.

tengstrand 2024-04-25T13:36:55.460719Z

Bases can depend on other bases, but that's quite unusual.

Vladimir Bokov 2024-04-25T13:38:42.533359Z

ok, got it, the projects/development/deps.edn -> ./deps.edn was forced by something like projectile looking for repl's deps.edn and trying to find files in this subfolder only? or any additional more important reason?

Vladimir Bokov 2024-04-25T13:57:52.604049Z

and second question - suppose we really want DI on ns level and use different components (`compA` and compB providing ns.comp.interface) should we have identical fn signatures in compA/src/ns/comp/interface.clj and compB/src/ns/comp/interface.clj ? (in short there's no defprotocol on ns-level which declares interface once)

tengstrand 2024-04-25T14:05:11.660339Z

tools.deps and other tooling want it at the root.

👌 1
tengstrand 2024-04-25T14:12:14.377539Z

Yes, the signature has to match. See https://cljdoc.org/d/polylith/clj-poly/0.2.19/doc/interface#one-interface-in-multiple-components example.

1
seancorfield 2024-04-25T16:34:12.213539Z

Since you @'d me... > but if I comment it out this single line, I'm still able to run clojure -M:dev -m usermanager.web.main and the app works Because :dev is how you specify the development project and it has "all" the dependencies defined in it. You could also run it via cd projects/usermanager && clojure -M -m usermanager.web.main > imo it's a bit error-prone and non-intuitive when you try to think about components as dependencies Depends on your mental model. Polylith talks a lot about "bricks" and assembling projects from those "bricks". At work, we have some components that have multiple implementations and the project decides which implementation is used (at build time). The development project can be run with any combination of those implementations but, because it is a build-time decision, you use aliases (`:+default`, etc) to tell the Clojure CLI which implementations to use for building the classpath, that is then used to run your project. Not having the (implementation) dependencies hard-coded in the bases or components is the key to making that possible. For example, we have an i18n component that has two completely distinct implementations: one uses a database, the other uses a local JSON file. We have one project that selects the JSON-backed version at build time to produce an app that can be run locally by our UI/UX folks so they can build out new HTML templates and provide the text strings in a local JSON file. That same component (interface) means that all the code that interacts with translations can work identically with both implementations, which improves reuse. We also have two implementations of our http-client component: one using hato that depends on JDK 11+, one using clj-http that can be run on JDK 8 -- because we have a single project that represents a legacy application that cannot run on JDK 9+. To give a sense of scale, we have 23 bases, 22 projects (plus development), and 162 component implementations, with 145k lines of Clojure altogether.

👍 2
👍🏻 1
Vladimir Bokov 2024-04-26T11:43:26.457419Z

thanks for detailed reply and usecases Sean, I got it, brick != dependency and not self-contained, basically you're also talking about namespace-level-DI as well let me generalize the use-cases: • for i18n making and passing a "translator" interface would be too invasive? • for http-client we cannot get a usual DI because there's no interface and classpath for projects and repls should be different anyway

seancorfield 2024-04-26T17:16:46.005079Z

The specifics for i18n is to be able to create an app that has no JDBC or database-related dependencies in it at build time so it produces a minimal JAR file.

👍 1
seancorfield 2024-04-26T17:21:20.848619Z

And for http-client, the hato-based version will not even load on JDK 8 -- but the clj-http version will load on both JDK 21 (11+) and JDK 8 so we can test both implementations on the more recent JDK if we wish -- but we do not generally want clj-http on the classpath because of its assumed dependencies -- so both implementation choices are build time by necessity.

👍 1
tengstrand 2024-04-25T04:57:46.470829Z

A Polylith song.

5
🤘 5