Data Access Object (DAO) Pattern in Clojure

Learn when a DAO layer actually helps in Clojure, how to separate domain logic from persistence without building generic repository boilerplate, and why `next.jdbc` is a cleaner modern fit than older JDBC wrappers.

Data Access Object (DAO): A boundary that isolates persistence logic behind a smaller application-facing interface.

DAO is useful when the rest of the system should not know how rows are fetched, updated, or mapped. In Clojure, though, the pattern should usually stay lighter than the object-oriented textbook version. Many systems only need a small namespace or protocol around database access, not a full inheritance hierarchy of repositories and entity managers.

The Real Goal Is Persistence Isolation

The strongest reason to introduce a DAO layer is not fashion. It is to keep domain code from depending on:

  • SQL details
  • table names
  • transaction plumbing
  • result-set quirks
  • database-specific driver behavior

That makes testing easier and gives the codebase one predictable place to change when the persistence model shifts.

In Modern Clojure, next.jdbc Is the Low-Level Default

Older examples often use clojure.java.jdbc, but most current Clojure projects prefer next.jdbc as the low-level JDBC library.

 1(ns myapp.user-store
 2  (:require [next.jdbc.sql :as sql]))
 3
 4(defprotocol UserStore
 5  (find-user-by-id [this user-id])
 6  (create-user! [this user-data]))
 7
 8(defrecord JdbcUserStore [ds]
 9  UserStore
10  (find-user-by-id [_ user-id]
11    (sql/get-by-id ds :users user-id))
12  (create-user! [_ user-data]
13    (sql/insert! ds :users user-data)))

This is already enough for many applications:

  • one small protocol or namespace
  • one concrete JDBC-backed implementation
  • one clear seam for tests

The point is not ceremony. The point is keeping persistence concerns contained.

DAO Should Speak the Domain, Not Just CRUD

A weak DAO layer often exposes only generic CRUD verbs. A stronger one reflects how the application actually works:

  • find-user-by-email
  • reserve-invoice-number!
  • list-open-orders-for-customer

That keeps persistence operations aligned with business use rather than with generic table mechanics.

Avoid Building an Abstract Repository Empire

In Clojure, it is easy to overbuild this pattern. If every table gets a giant generic repository abstraction with little domain meaning, the DAO layer becomes harder to understand than plain SQL namespaces would have been.

Prefer:

  • small store namespaces or protocols
  • query functions that return clear shapes
  • explicit transaction handling at useful boundaries

Avoid:

  • generic repositories that hide too much
  • one abstraction shared across unrelated persistence needs
  • domain code that still has to understand raw row shapes anyway

Rows, Domain Values, and Mapping

Another useful DAO responsibility is mapping between storage shape and domain shape. That might mean:

  • keywordizing column names
  • turning DB enums into domain keywords
  • hiding join complexity behind a simpler returned map

If callers must manually decode every row shape after retrieval, the persistence seam is only half-built.

Common Failure Modes

SQL Spread Across the Whole Codebase

Once every namespace builds its own queries, persistence rules become hard to change and harder to test.

Generic CRUD Abstractions With No Domain Language

The DAO exists to express useful data access boundaries, not just to wrap insert, update, and delete.

Returning Raw Driver Shapes Everywhere

If callers still need to understand JDBC details or low-level row formats, the DAO has not done enough useful shaping.

Practical Heuristics

Use a DAO boundary when persistence details would otherwise leak into domain code. In Clojure, keep the layer small, prefer next.jdbc for low-level JDBC work, and expose domain-meaningful query functions rather than a generic repository framework. The best DAO is the one that isolates the database without making the codebase more ceremonial than the problem requires.

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026