Created
December 13, 2024 10:48
-
-
Save ferdinand-beyer/a706c58c39088630c96c2338cc34846e to your computer and use it in GitHub Desktop.
Example DDD framework for Clojure + Datomic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns app.service | |
(:require [domain.user :as user] | |
[support.app :as app])) | |
(defn ensure-user-exists! [username full-name] | |
(let [ctx (app/new-context)] | |
(if-let [user (user/find-user-by-username ctx username)] | |
user | |
(let [ctx (-> ctx | |
(user/create-user username full-name) | |
(app/commit!))] | |
;; TODO: We could provide some mechanism to resolve tempids from previous | |
;; transaction. | |
(user/find-user-by-username ctx username))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns domain.user | |
(:require [datomic.api :as d] | |
[support.domain :as domain])) | |
(defn find-user-by-username [ctx username] | |
(d/pull (domain/db ctx) '[*] [:user/username username])) | |
(defn create-user [ctx username full-name] | |
(-> ctx | |
(domain/add-tx-data [{:user/username username, :user/full-name full-name}]) | |
(domain/log (str "Created user '" username "'")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns support.app | |
(:require [datomic.api :as d] | |
[support.domain :as domain])) | |
(defprotocol IUnitOfWork | |
(commit! [unit-of-work])) | |
(deftype LoggingContext [logs] | |
domain/ILogContext | |
(log [_ message] | |
(LoggingContext. (conj logs message))) | |
IUnitOfWork | |
(commit! [_] | |
(run! logging/log! logs) | |
(LoggingContext. [])) | |
(defn- new-logging-context [] | |
(LoggingContext. [])) | |
(deftype DatomicContext [conn db tx-data] | |
domain/IDatomicContext | |
(db [_] db) | |
(add-tx-data [ctx new-tx-data] | |
(DatomicContext. inst logs conn db (into tx-data new-tx-data))) | |
IUnitOfWork | |
(commit! [_] | |
(let [{:keys [db-after]} @(d/transact conn tx-data)] | |
(DatomicContext. conn db-after []))))) | |
;; We assume that this gets initialised somewhere... | |
(def ^:dynamic *datomic-conn* nil) | |
(defn- new-datomic-context [] | |
(let [conn *datomic-conn*] | |
(DatomicContext. conn (d/db conn) []))) | |
(deftype Context [inst log-ctx db-ctx] | |
domain/ITimeContext | |
(now [_] inst) | |
domain/ILogContext | |
(log [_ message] | |
(Context. inst (domain/log log-ctx message) db-ctx)) | |
IDatomicContext | |
(db [_] (domain/db db-ctx)) | |
(add-tx-data [ctx tx-data] | |
(Context. inst log-ctx (domain/add-tx-data db-ctx tx-data))) | |
IUnitOfWork | |
(commit! [_] | |
(Context. (java.time.Instant/now) | |
(commit! log-ctx) | |
(commit! db-ctx))) | |
(defn new-context [] | |
(Context. (java.time.Instant/now) | |
(new-logging-context) | |
(new-datomic-context))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns support.domain | |
"Infrastructure for the domain layer.") | |
(defprotocol ITimeContext | |
(now [ctx] "Returns the current time instant.")) | |
(defprotocol ILogContext | |
(log [ctx message] "Logs a message.")) | |
(defprotocol IDatomicContext | |
(db [ctx] "Returns the db value.") | |
(add-tx-data [ctx tx-data] "Adds tx-data to transact.")) | |
;; Example of additional context protocols: | |
(defprotocol IAuthContext | |
(user [ctx] "Returns the authenticated user.")) | |
;; Go-style context to support cancellation / deadlines | |
;; (but not pure) | |
(defprotocol ICancelableContext | |
(canceled? [ctx]) | |
(deadline [ctx])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment