Browse Learn Clojure Foundations as a Java Developer

Organize Source and Test Directories

Organize `src`, `test`, and `resources` directories around classpath roots, mirrored test namespaces, and the differences from Maven or Gradle source sets.

Java projects often separate code with Maven or Gradle source sets such as src/main/java and src/test/java. Clojure projects usually use a flatter layout: src/ for production namespaces, test/ for test namespaces, and resources/ for files that should be available on the JVM classpath.

The important idea is not the folder name itself. The important idea is the classpath root. A namespace path is resolved relative to one of the roots your tool puts on the classpath.

Directory Role
src/ Production Clojure namespaces, similar to Java’s src/main/java source role
test/ Test namespaces, usually added for test runs rather than production packaging
resources/ Classpath files such as EDN defaults, logging config, templates, and static assets
dev/ REPL helper namespaces and local workflow code added only by dev aliases or profiles

A Small Production Namespace

Suppose the production namespace is order-service.pricing. Its file should live under a source root:

1(ns order-service.pricing)
2
3(defn subtotal [order]
4  (reduce + (map :price (:items order))))
5
6(defn total [order]
7  (* (subtotal order) 1.13))

The path is:

1src/order_service/pricing.clj

Notice again that order-service becomes order_service on disk.

A Matching Test Namespace

Tests normally mirror the production namespace and add -test to the namespace name. That becomes _test in the file name.

Path:

1test/order_service/pricing_test.clj

File:

1(ns order-service.pricing-test
2  (:require [clojure.test :refer [deftest is testing]]
3            [order-service.pricing :as pricing]))
4
5(deftest subtotal-adds-item-prices
6  (testing "subtotal is a pure calculation over order data"
7    (is (= 20
8           (pricing/subtotal {:items [{:price 12} {:price 8}]})))))

This style gives you the same fast navigation benefit as Java test packages: if you know the production namespace, you can predict the test namespace.

How Tools Put Directories on the Classpath

With the Clojure CLI and deps.edn, :paths identifies normal classpath roots. Test and development roots are commonly added through aliases so production runs do not accidentally depend on test code.

1{:paths ["src" "resources"]
2 :deps {org.clojure/clojure {:mvn/version "1.12.5"}}
3 :aliases
4 {:dev {:extra-paths ["dev" "test"]}
5  :test {:extra-paths ["test"]}}}

Leiningen projects use project.clj instead, but the mental model is similar: production source, test source, resources, and development-only additions are separate inputs to the JVM classpath.

Compare Java and Clojure Layouts

Java source-set idea Clojure equivalent
Production source under src/main/java Production namespaces under src
Test source under src/test/java Test namespaces under test
Resources under src/main/resources Classpath resources under resources
Package maps to directories Namespace maps to directories and file names
JUnit, TestNG, or AssertJ clojure.test or library-specific runners

Clojure’s flatter layout can look too informal at first. In practice, the discipline comes from namespaces, aliases, and classpath roots rather than nested Java source-set folders.

Resource Files

Keep files in resources/ when code should load them through the classpath. Examples include default EDN configuration, migration templates, logging configuration, and small static assets used by the application.

1(ns order-service.config
2  (:require [clojure.edn :as edn]
3            [clojure.java.io :as io]))
4
5(defn read-defaults []
6  (some-> "defaults.edn"
7          io/resource
8          slurp
9          edn/read-string))

The Java analogy is ClassLoader#getResource, not reading a relative file from the current working directory. That difference matters in packaged applications, Docker images, tests, and CI.

Review Checklist

Before you call a Clojure project “organized,” check these points:

Check What to look for
Source path Namespace path is predictable under src/ and matches the file name.
Test path Test namespace mirrors the production namespace rather than living in a flat miscellaneous folder.
Resources Runtime files are loaded from resources/ instead of the shell working directory.
Dev helpers REPL-only code is under dev/ or a dev alias, not required by production startup.
Classpath config deps.edn aliases or Lein profiles explain which roots are active in each command.

Practice

Take one Java package you know well and sketch a Clojure equivalent:

  1. Pick one production namespace for pure calculations.
  2. Pick one boundary namespace for HTTP, database, or Java interop.
  3. Put tests in mirrored *-test namespaces.
  4. Put default EDN or resource files in resources/.
  5. Verify each namespace can be required from a clean REPL or test run.

Knowledge Check

### Why does Clojure usually use `src/` instead of `src/main/java`? - [x] Clojure projects use classpath roots and namespace paths rather than Java's source-set convention - [ ] Clojure cannot run from nested directories - [ ] The JVM ignores folders named `main` - [ ] Clojure tests must live beside production functions > **Explanation:** The convention is flatter, but still disciplined. A namespace path is resolved relative to a classpath root such as `src/`. ### What test file normally corresponds to `src/order_service/pricing.clj` with namespace `order-service.pricing`? - [x] `test/order_service/pricing_test.clj` - [ ] `test/order-service/pricing-test.clj` - [ ] `src/order_service/pricing_test.clj` - [ ] `test/order/service/PricingTest.clj` > **Explanation:** The test namespace is usually `order-service.pricing-test`, so the file path uses `order_service/pricing_test.clj`. ### Why should runtime resources usually be loaded from `resources/` instead of a relative filesystem path? - [x] Classpath loading keeps the code stable across REPLs, tests, packaged runs, and deployment environments - [ ] `resources/` automatically converts EDN into Clojure functions - [ ] Relative filesystem paths are never allowed on the JVM - [ ] Test code cannot read files outside `resources/` > **Explanation:** Working-directory assumptions often break in CI or packaged applications. Classpath resources behave more like Java resources and are easier to package reliably.
Revised on Saturday, May 23, 2026