poly
Search
⌃K

Component

Now when we have a working development environment, let's continue and create our first component. But before we do that, open workspace.edn in a text editor and set :auto-add to true:
...
:vcs {:name "git"
:auto-add true}
...
By doing this, we want to ensure that the created files and directories from the create command is also added to git, which will come in handy in this example (normally we will leave the setting as it is).

How to execute commands

Before we continue with creating a component, let's say a few words about the different ways of executing commands. We have described three ways already, e.g.:
  1. 1.
    poly info - using the compiled version of the tool.
  2. 2.
    clojure -M:poly info - using Clojure CLI from the workspace directory.
  3. 3.
    clojure -Tpoly info - as a Clojure CLI Tool
All the documentation refers to the first form, even though the other two are also valid. There is also a fourth way, and that is to execute the command from a shell. We can start an interactive shell by typing poly:
From here it's enough to enter the name of the command, e.g.:
This way of executing commands is the most convenient way in most cases, because it gives us instant feedback, history, and autocomplete. Feel free to use the shell from now on. It will make you more productive and enhances the development experience!
Now let's continue with our example by executing the create component command:
poly create component name:user
Our workspace will now look like this:
example
├── bases
├── components
│ └── user
│ ├── deps.edn
│ ├── resources
│ │ └── user
│ │ └── .keep
│ ├── src
│ │ └── se
│ │ └── example
│ │ └── user
│ │ └── interface.clj
│ └── test
│ └── se
│ └── example
│ └── user
│ └── interface_test.clj
├── deps.edn
├── development
│ └── src
│ └── dev
│ └── lisa.clj
├── logo.png
├── projects
├── readme.md
└── workspace.edn
The command also printed out this message:
Remember to add paths and/or local/root dependency to dev and project 'deps.edn' files.
This was a reminder for us to add the component to deps.edn. If we don't, then tools.deps and the development environment will not recognize our newly created component, which would be a pity! The tool leaves this task to you as a developer, with the idea to give you as much control as possible (files are only edited by you, not by the tool).
Right now we can ignore the last part of the message, to add the component to project deps.edn files, because no projects have been created yet.
Let's continue by adding the component's src, resources and test directory to deps.edn:
:aliases {:dev {:extra-paths ["development/src"
"components/user/src"
"components/user/resources"]
...
:test {:extra-paths ["components/user/test"]}
An alternative way of adding the component is by specifying it as an :extra-deps (the development/src directory still has to be specified as a path):
:aliases {:dev {:extra-paths ["development/src"]
:extra-deps {poly/user {:local/root "components/user"}}
...
:test {:extra-paths ["components/user/test"]}
If you use Cursive as an IDE, this will not work correctly, and the problem is that Cursive doesn't treat components/user/src as a source directory in the IDE (it will not be marked as green). This is also why we use the first form in this example.
However, in many other IDE's like VSCode/Calva and Emacs/Cider this works fine, which also gives us some benefits:
  • Less code, one line instead of two.
  • It's more consistent with how projects are specified.
  • You can add or remove the resources directory from a brick, without updating ./deps.edn.
With that said, we will still specify user by using :extra-paths because we use Cursive in this example.
Notice that we still need to add the test directory to ./deps.edn to be able to run the tests.
Now we may need to refresh our IDE, by clicking this link, or the icon we used before:
The component also has its own deps.edn file that looks like this:
{:paths ["src" "resources"]
:deps {}
:aliases {:test {:extra-paths ["test"]
:extra-deps {}}}}
It specifies that it has a src, resources and test directory and will later be needed by the projects that include this component.
Now execute the info command:
poly info
This tells us that we have one development project, one user component and one user interface but no base (yet). Components and bases are referred to as bricks (we will soon explain what a base is). The cryptic s-- and st- will be described in the flags section.
If your colors don't look as nice as this, then you can visit the colors section.

Add implementation

Now, let's add the core namespace to user:
...and change it to:
(ns se.example.user.core)
(defn hello [name]
(str "Hello " name "!"))
...and update the interface to:
(ns se.example.user.interface
(:require [se.example.user.core :as core]))
(defn hello [name]
(core/hello name))
Note: Use the copy icon when copying the text from these examples, otherwise all the lines will be copied into one single line, and the "new line" characters will not be included. If you use Firefox, you need to switch to another browser (it doesn't work in Firefox).
Here we delegate the incoming call to the implementing core namespace, which is the recommended way of structuring the code in Polylith. Here we put all our implementing code in one single namespace, but as the codebase grows, more namespaces can be added to the component when needed. The implementing core namespace can be renamed to something else, but here we choose to keep it as it is.