Welcome! Please see the About page for a little more info on how this works.

0 votes
in Clojure by

tl;dr

One of the things I actually like about JS is that I can declaratively define the interfaces/boundaries of folder-level modules through defining a public interface by re-exporting what's supposed to be accessible to other modules and I'm wondering if there's something similar in Clojure (and if not - what's the best way to request a feature).


EDIT: Don't know why the formatting isn't working, the question's preview looks fine...

Let's say there's the following folder structure:

| my-project/ |-- src/ |---- product/ |------ product_related_module/ |------ other_product_related_module/ |---- catalog/ |------ catalog_related_module/ |---- checkout/

I don't want the catalog and checkout modules to know anything about internal things of the product module. Instead I want the product module (all modules actually) to declare its interface, which to me means that there's a file that explains what's being exposed and supposed to be used.

In JS, I can define whatever is supposed to be publicly by using index.js files that do only one thing: re-exporting what's supposed to be public. Everything else is considered internal (can be enforced with eslint).

This helps tremendously to maintain a loosely coupled and strongly cohesive project. With this, the internal structure of the src/product module can be re-organized in any way, as long as the interface does not change, the rest of the application doesn't need to care.

I've seen many clojure examples like this:

`clj
(ns my-project.catalog.catalog-related-module.get-catalog-page
(:require [my-project.product.product-related-module.foo :as foo]))
`

So I'm wondering if there's a way to add re-exporting files, so I could do this:

`clj
(ns my-project.catalog.catalog-related-module.get-catalog-page
(:require [my-project.product :refer [foo]]))
`

In javascript I could achieve this by using index.js files:

`js
// src/product/product-related-module/index.js
export { foo } from './foo.js'

// src/product/index.js
export { foo } from './product-related-module/index.js'
`

Ideally I could even restrict from requiring anything from a namespace inside the product-namepsace when accessing from outside.

1 Answer

0 votes
by

Direct re-exporting is possible with third-party tools or your own macros, but is not recommended for various reasons.

A reasonable approach is to have src/app/product.clj (I added app so it's not a single-segment namespace) or src/product/api.clj. Each function that must be a part of the API would then be either directly implemented in that file or "aliased", as in (def public-fn some-other-ns/public-fn). The latter is suboptimal as it requires extra navigation when you study the sources and it makes run-time introspection and static analysis harder.

...