I made it to my first runnable JAR file! Still reading the (long) README...
I have an idea about the interface naming convention @seancorfield. In workspace.edn
we currently support setting the name of the interface with the :interface-ns
attribute, where āinterfaceā is the default value. My idea is to support another option where the interface name always has the same name as the the interface, by e.g. setting interface-ns
to :interface-name
so if the name of the interface is database
then it will expect to find a database.clj
namespace at the top level (which would be the interface of that component). Then you are free to put the implementing namespaces wherever you want, e.g. at the top level and/or in sub namespaces. We could also support setting interface-ns
to :component-name
so that if you have two components that implements the invoice
interface, e.g. invoice
and invoice-remote
, their respective namespaces would be`invoice.clj` and invoice_remote.clj
. Iām not sure whatās best here, and if we should support only one behaviour (then all Polylith codebases would act the same which has its benefits) or if people would prefer to choose one of the three different options (or maybe restrict it to two). We havenāt discussed this within the Polylith team yet, but I like the idea so far.
I think that's a good "alternate behavior".
I think it addresses the random/arbitrary naming concern and it makes the code easier to navigate in an editor.
I'm thinking about the :interface-name
vs :component-name
... I don't feel I've had enough experience with Polylith to comment, but given you can create component name:... interface:...
I'm not sure how :component-name
would work?
:interface-name
feels like what I would want based on my (small) experience today -- and given how we already organize our monorepo.
(BTW, the biggest difference between our monorepo and Polylith is that we don't distinguish between bases, components, and projects -- those all exist as subprojects at the top-level under their unique names)
I have the same feeling, that :interface-name
+ āinterfaceā would be enough.
Ok.
Yes, you get the sharing with the monorepo, but to replace things (and the encapsulation) you need the interfaces.
Yeah, that replacement isn't a pressure we've found (in a decade of work with, now, 112K lines of code).
We don't really draw a distinction between what Polylith calls bases and projects either, to be honest. Ours have always had a 1:1 relationship. So far š But it's an interesting distinction to consider.
So basically, to get Lego-like bricks, you need all of them: monorepo, project, base, component, interface. Maybe you will found pieces of code in your 40 bases that could be extracted into components and shared across these 40 projects or that you will divide these bases into components instead of only using namespaces within bases. Time will tell! š
The Polylith codebase has 25 components, 3 bases, shared across five projects/artifacts + the development project:
For example the file
component is responsible for accessing the file system and is used by all projects. This is quite common, that you have common functionality across projects/artefacts that can be put into a component and shared across projects.
We only have about 14 "bases" in your terms but, yes, the other 27 "components" include several that are bigger than Lego š
It would be interesting to see your project, so if yo want, you could execute this and mail me: poly ws out:dot-clojure.edn
because then I can type poly info ws-file:dot-clojure.edn
and get an idea!
(or if you have pushed into a branch already)
We've also made a pragmatic choice to only build one artifact that contains several -main
functions that provide a slew of cron jobs. Deploying a separate JAR for each process would add more complication than benefits. I suspect you might consider each of those jobs to be a separate project?
Not sure what you're referring to with that dot-clojure
reference?
And poly
wouldn't run in our work project because it's not a "poly workspace"
Ok, thought you converted the dot-clojure
project into a Polylith workspace, but maybe you worked on another project when testing Polylith.
https://github.com/seancorfield/dot-clojure <-- this is what I'm referring to by dot-clojure; it's my user-level deps.edn
file (and a support script dev.clj
).
It's not a "project" with code.
The api
artifact can answer the question what projects that have changed since the last successful build. Then itās up to the build script to handle all projects i one build or to have separate build projects.
It's just developer conveniences for work with every project.
Okay, now I see!
It's where folks who use the CLI a lot would typically put all of their developer tooling that isn't specific to a project.
Ok, I get it.
Practicalli has one too https://github.com/practicalli/clojure-deps-edn (much better documented and intended more for beginners to learn the CLI with).
If a Polylith project is going to be entirely in-house and never publish libraries, it would seem that the :top-namespace
isnāt really needed. Can it be nil
so users can use whatever full namespace names they want?
I ask because I realized that at work we have older namespaces as worldsingles.*
and some newer ones as ws.*
ā most of our newer apps are ws.<project>
for the top-level entry point ā but we also have some <project>.*
ones. Yes, I know, potential conflicts yadda yadda yadda, but Iām just wondering about how much flexibility Polylith could allow in that areaā¦
As a bystander in this discussion I love the way Sean is pushing polylith to be less strict without losing the important parts.
Thanks. Iām trying to find what aspects of Polylith add value over the monorepo/deps setup with already have. And I have to say that the command-line tool that track dependencies and git status and what needs testing is very interesting (we have some of that in an assortment of scripts but itās pretty ad hoc).
Iām an old skeptic so I need to be convinced something ārestrictiveā really does improve things š
Yeah, I worked in a monorepo and we also had lots of adhoc scripts to only test necessary code but I would like to have a standard for that. I think the problem is other people restrictions, we are usually fine with the ones we come with. š
We have a general shell script (called build
) that does some of what poly
does including ārun all tests that affect this āprojectāā and ārun tests for all subprojects affected by this changed subprojectā ā but our subprojects are pretty coarse-grained and our deployment artifacts (uberjars) and ābigā ā in my mind, at least: a couple of them are 70MB but the smallest are under 30MB and Iād like to tease apart our codebase to produce smaller artifacts, perhaps even if it means more artifacts. But converting it to a Polylith structure would be a massive undertaking at this point. If I can break up things into more ācomponent-sizedā pieces, that will be a win.
I actually implemented support for having arbitrary top namespace per brick (component and base) earlier, in a separate bransch. We decided to not merge that code because it made the Polylith code more complex but maybe more importantly, it complicated the use of Polylith and as you said @seancorfield it also opened up for naming clashes. But yes, it is possible.
It wouldnāt be a bad thing for us to adopt at least a standard short ns prefix at work butā¦ this is a codebase that goes back over ten years and we were new to Clojure back thenā¦ ĀÆ\(ć)/ĀÆ
You can put the code for each āproject/artefactā that you have today @seancorfield into a base + make sure that all of them use the same top namespace, e.g. com.company
and then each base would live in e.g. com.company.mybase.
You would have to refactor the namespaces for each base to conform to that pattern. Then you could create a project for each base and include only that base in each of them. That should be doable.
I was thinking more:
* Anything that starts with ws
leave alone
* Anything else rename to add ws.
prefix
* Over time, rename ws.worldsingles.*
to something more appropriate as I move code around š
You donāt need to convert the whole codebase, you could start with one base and then migrate one āprojectā at a time.
But, as Iāve noted before, we have what you would call ābasesā, that have namespaces like ws.billing
so our repo structure tends to be like wsbilling/src/ws/billing.clj
and the immediate implementation is ws.billing.stuff
š
We decided to also support your suggested naming convention of the interfaces, by the way!
Oh, cool! I think Polylith will be more approachable for folks if it looks a bit less like a set of āweirdā naming conventions and directory structure and more like a āreasonableā convention/structure whose goal is to make tooling easier to write/use ā and then it wonāt distract from the architecture portion which is the Lego-like aspect of it.
I donāt really agree that the four top directories components
, bases
, development
, and projects
are weird. Itās kind of the opposite. It really helps you understand and reason about the codebase, not the least if you are new to a codebase. I know that you like your setup that you are used to, but having a directory called components
with letās say 50 directories with descriptive names and one entry point (the interface) is actually super helpful. @furkan3ayraktar has worked with several Polylith projects (around five) and the benefit you get by this structure has a lot of value.
I know you donāt think they are weird, but this is part of why several folks react to Polylith as āa naming conventionā or a ādirectory structureā š
I will say that we find having the top-level directories in a monorepo being related to the business or functionality is ānaturalā for us (and it seems to be the more common monorepo structure).
@tengstrand Can you remind me what the thinking was behind have projects
having deps.edn
vs being aliases in the root deps.edn
? I think you explained but canāt find it in the chat history so I donāt know where we discussed it.
There are a reason why we use interfaces, and that is the same reason interfaces are used in OO, to decouple parts of our system and to be able swap what concrete implementations to use without affecting the consumer of that interface. That is an example of why we use different concepts like interfaces. They are there to give you more value and in the end to help people to work efficiently with the code and to be able to easily arrange how to execute the code in production. As I have said before, itās like Clojure, itās hard to convince people that are used to work in a different way (like OO) but when you have used it for a while, you start to appreciate the fast feedback loop and the Lego-like feeling it gives you.
@tengstrand Just checking: Did you miss this Q? Can you remind me what the thinking was behind projects
having deps.edn
vs being aliases in the root deps.edn
? I think you explained but canāt find it in the chat history so I donāt know where we discussed it.
@seancorfield @pavlos Each project may have its own tests and resources associated with it.
So instead of adding everything as aliases at the root deps.edn
,
we put everything for each project in its own place.
For example, if you have ten projects where each has several aliases,
src, tests, resources, and dependencies specified, the root deps.edn
file
would get huge. If we choose to have everything in the root deps.edn
,
to be able to distinguish between development related aliases and
project specific aliases, we would also need to prefix the latter,
which would result in extra complexity.
To let each project live in a separate folder also allow us to
easily recognise changes that effects only a specific project,
which is used by the poly
tool to support incremental testing.
This is a design choice we have made, because we believe this is simpler
and follows the single responsibility principle.
You are right about that when we build an artifact from a project, we only include the source code from the bricks. Itās also true that you have the opportunity to put some shared library dependencies at the project level (e.g. Clojure itself, or logging). There is one case when we sometimes need to add code to a project, and that is when we add tests that is specific to that project. In that case we will add a test
directory to the project containing these tests. There is a case when you will ābake inā extra files/resources to the final deployable artifact, and that is if you have the need to have project specific resources. In that case you will also add resources
at the project level.
See the main thread for my progress with a clj-new template for polylith -- and problems running project tests.
Yes, looking at it now.
I think it was to do with some restriction in t.d.a / deps.edn
but I canāt remember the detailsā¦
At least the last time we checked, the IDE we used didnāt pick up the test
directories for the bricks when we used the`:local/root`syntax, which affected the development experience negatively. For the other projects, we use our own test runner to solve that problem, which means that we can include the bricks as dependencies.
The reason we put the paths in the`:dev` alias as extra-paths
in ./deps.edn
is that we wanted to isolate the dependencies we use for development into one place.
That doesnāt sound like an answer to my question. Iām not sure how weāre miscommunicating here.
Okay, now I understand your questionā¦
Iāll try from another side: in Polylith, it seems that you have to cd projects/myapp
and then run clojure
there to do the compile/packagingā¦ we do it via aliases in the root deps.edn
file so we donāt have to cd
, we can build directly from the root folder.
Perhaps partly because we use depstar
and it handles the classes
folder and does AOT etc all in one place?
(it actually uses a temp directory for classes
so it doesnāt clutter up your workspace)
A project can contain many bricks and also aliases. If you have many projects, we liked the idea to let each project have their own deps.edn
file instead of putting everything into one huge deps.edn
file. But maybe you just mean the ābuildingā aliases?
So far, we donāt require people to install any extra tooling like depstar
or similar to be able to work with the code and to build it (the poly
tool only makes it more convenient, but itās not required).
Weāre still not communicating.
Maybe a video meeting some day could be an alternative?
Iāll go back search the chat history in various places and see if I can find the comments you made about projects
that Iām referring to.
Ok
The way I see it, polylith projects are independent entities with public apis, so it makes sense for their dependencies to also be decoupled from the root deps.edn.
As in, let's say you're developing an rss client. You could have 4 polylith projects: rss feed api, cli, desktop, web
From the perspective of the user, would it be better to have a single deps.edn for all of them or keep them separate? Edit: by "user" I meant anyone who might want to use your code. That could include future you, or the maintainer of another repo.
Not sure if it helps at all in the discussion (feel free to ignore it if not) but that's my $0.02 :man-shrugging: