This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-11-03
Channels
- # announcements (35)
- # aws (20)
- # babashka (4)
- # beginners (88)
- # cider (9)
- # clara (1)
- # clj-kondo (6)
- # cljsrn (3)
- # clojure (107)
- # clojure-dev (7)
- # clojure-europe (99)
- # clojure-nl (3)
- # clojure-spec (9)
- # clojure-uk (2)
- # clojurescript (28)
- # core-async (53)
- # cursive (11)
- # datascript (1)
- # datomic (2)
- # emacs (20)
- # fulcro (3)
- # graalvm (4)
- # holy-lambda (18)
- # jobs (1)
- # kaocha (7)
- # leiningen (2)
- # lsp (25)
- # luminus (1)
- # membrane-term (52)
- # missionary (8)
- # nextjournal (19)
- # off-topic (16)
- # other-languages (3)
- # podcasts-discuss (2)
- # polylith (23)
- # re-frame (4)
- # reclojure (6)
- # remote-jobs (1)
- # rewrite-clj (36)
- # ring (1)
- # sci (10)
- # shadow-cljs (7)
- # spacemacs (5)
- # sql (20)
- # uncomplicate (1)
- # vscode (3)
- # xtdb (27)
I am still in the first step in the migration from a monolith and I am a bit confused by the way dependencies (or libraries in polylith linguo https://polylith.gitbook.io/poly/workflow/libraries) should be organized.
My first assumption was that the libraries should be added as deps/extra-deps in the smaller brick: project > bases > components. Being at the first step of the migration I put all the libraries in the base. It worked for the project tests, but when trying to kick off a repl with the dev and test profiles I'm now missing dependencies.
Do all libraries have to be duplicated in the extra-deps of the top-level deps.edn
to have them available in the development project?
What I find odd with that pattern is that if one updates a dependency only in ./deps.edn, it will use the new version in development, but will use the old one in the projects?
It depend how your root deps.edn
looks like. If you have a setup where the dev alias of your root deps.edn
lists the components and bases as :local/root
in the :extra-deps
, then the development REPL will get all the dependencies from the individual components and bases. However, if you are listing the components and bases in the :extra-paths
, then you need to duplicate all the dependencies again in the deps.edn
. Polylith relies on the default behavior of tools.deps when it comes to resolving dependencies.
Answer to second question: Polylith does not check the dependency versions for you, you should always make sure you have what you want in any project, whether it’s development or production. What Polylith provides is the flexibility to change dependencies for your development or for your projects. You can also define libraries in the project level deps.edn
to set them to a certain version, overriding the version that comes from your component, if needed. That is handy if indirect dependencies in different components have different versions, so you can choose which version to use at the project level.
Also, if you have tests that fail due to different versions in development vs project, you’ll catch them when the project level tests run.
At work we use the :extra-deps
approach in :dev
with :local/root
so each brick can have its own (external) library dependencies. Then the :dev
alias only contains any additional/specific dependencies we want for development -- which is a specific set of logging libraries and a JDBC driver.
Then each project specifies just those extra dependencies (and, in one case, pins a specific version of the Jackson libraries to avoid a breaking change that breaks just one of our apps). This allows for different databases for dev vs built projects and different logging setups (although, right now, we are using the same ones in all but one project).
@U2BDZ9JG3 I tried the local/root path already, and noticed it wasn't working for the extra deps of the test profile of my base, I will try again
I think @U04V70XH6 has the best setup that you can have in regards to handling the library dependencies. That is how Polylith intended the :extra-deps
and :local/root
to be used.
@U0DB715GU In any case, either :local/root
in :extra-deps
or with :extra-paths
you need to have a :test
alias in your root deps.edn
and start your REPL with at least :dev
and :test
aliases in order to get test paths are included in your REPL session. If you have any library dependencies in components/bases :test
alias, you need to duplicate them to :extra-deps
in :test
alias of the root deps.edn
.
The reason for this is, :local/root
does not bring the aliases.
It would be great if tools.deps
supported something like {:local/root "components/comp-1" :aliases [:test]}
, then you wouldn’t need duplication test paths and dependencies.
Thank you both for the super clear explanations! I think incorporating that to the doc might be quite useful for others attempting the jump
Is that still true? I thought that poly test
brought in dependencies from :test
in bricks when building the classpath for testing projects (including development)?
@U04V70XH6 it does for poly test
, I didn't specify the test dependencies in my project and the tests are running fine with it. What doesn't work is running the tests from the repl after starting it with dev and test aliases in the development project
Ah, right. I think we must have very few test-only dependencies at work -- at least in our Polylith code... our legacy code definitely has some test-only dependencies so we probably just haven't run into this. Our :test
alias is:
:test ; Polylith's testing context for the development project
{:extra-deps {worldsingles/worldsingles-test {:local/root "worldsingles-test"}
com.github.seancorfield/expectations {:mvn/version "2.0.0-alpha2"}
org.clojure/test.check {:mvn/version "1.1.0"}}
where worldsingles-test
is a legacy subproject that we haven't migrated yet and it brings in a lot of external deps via its dependencies.Do you have all your projects in one Polylith workspace? Do you try to keep all components as versatile as possible? Or do you separate components by project like https://github.com/furkan3ayraktar/clojure-polylith-realworld-example-app/tree/master/components/article/src/clojure/realworld. My understanding of this example is that /article/ contains the generic components that the developer associates with that word, and /realword/ further specifies the meaning - which is determined by the project with the same name. Right?
I am about to migrate my projects to Polylith but i am still not sure what is the good way to organize all components i will create / extract.
As a user still early in Polylith adoption: We don't, but we have considered it and we already know we have a couple of projects we will integrate into our main polylith because they have slightly cross-cutting concerns and there's potential for code reuse. The others will remain in the monorepo but outside of the polylith (which is its own folder).
If there's little to no potential to reuse code, I probably would not introduce it there. Mainly because I worry about polluting the component space too much with little to show for it and just end up creating components like projectA-billing
and projectB-billing
when there's a conceptual overlap but implementations that need to be clearly different. Not sure if that worry is justified, but I'm instinctually inclined to believe two isolated trees (or workspaces, in Polylith lingo) would be cleaner if they really don't have too much to do with each other.
We have 6 projects, more than 100 components in a single Polylith workspace. Our components have single responsibility and usually around couple hundred lines. We create projects and bases when the non-functional requirements change: e.g. a specific part of the codebase needs to scale differently than other parts, then a new project is created and has its own deployable artifact. We keep everything in one workspace and intended to continue that as a principle because we love to have a single development environment that gives us a REPL with access to all of our code. Very easy refactoring and debugging. We keep components small and they are responsible of one thing. 1) It makes them easily replaceable, if needed. If there is a logger component with a function log, its easy to write a replacement component that has same signature but using another logging library. 2) It makes them very reusable, if we need the set of functions that a component exposes anywhere, we require it.
@U2BDZ9JG3
This is why I would like to start using Polylith. It seems to me that the combination of modular codebase + repl can offer a developer an environment that resembles Smalltalk - an extensible image/workspace from which different applications emerge.
But I’m still trying to understand this namespace from the realworld demos.
(ns clojure.realworld.article.core)
clojure. you are using to decouple from CLJS I assume.
But why is it part of the realworld namespace if the components are supposed to be universal and used across projects (and realworld is just one of the projects, right?).
thx
clojure.realworld
is the top-namespace for that specific workspace, so every namespace in the workspace needs to be under that top-namespace (https://github.com/furkan3ayraktar/clojure-polylith-realworld-example-app/blob/master/workspace.edn). The poly CLI tool does static code analysis to provide many features and it uses the top-namespace to differentiate namespaces from external dependencies from the components and bases.
The specific words in clojure.realworld
is totally my personal choice. Realworld has implementations in many different languages, so, I thought it would be nice to indicate it in the namespace. It has nothing to do with Polylith.
You can interpret this namespace clojure.realworld.article.core
as:
workspace top namespace: clojure.realworld
component name: article
Similar with bases, e.g.: clojure.realworld.rest-api.main
workspace top namespace: clojure.realworld
base name: rest-api

A little clarification. Often the component name and its interface share the same name as in this example (`article`) but to be correct, article
in clojure.realworld.article.core
is actually the interface
name, and if we create another component, let’s say another-article
we can let it implement the same article
interface and if it also had a core
namespace, then it would also live in clojure.realworld.article.core
. One would live under components/article
and the other one under components/another-article
but they would share the same clojure.realworld.article
“interface top namespace”.
