Skip to content

Instantly share code, notes, and snippets.

@jcromartie
Created January 15, 2014 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcromartie/094b36e20132c970fbc0 to your computer and use it in GitHub Desktop.
Save jcromartie/094b36e20132c970fbc0 to your computer and use it in GitHub Desktop.
CQRS/event-sourcing, because that's the cool thing to do right?
(ns ecc2.data
(:require [clojure.java.io :as io]
[clojure.tools.logging :as log]))
(defprotocol Event
(affect [this state] "Return new state from given state."))
(defprotocol EventStore
(events [this] "Returns a seq of all events in the store.")
(store [this event] "Append an event to the store."))
(extend-protocol Event
;; Implement Event protocol on sequential collections to allow
;; grouping of events in a single transation:
clojure.lang.Sequential
(affect [coll state]
(reduce #(affect %2 %1) state coll)))
;; Implement EventStore on nil to allow simple in-memory db:
(extend-protocol EventStore
nil
(events [_] nil)
(store [_ _] nil))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data system functions.
;; The data system is just the read-model ref and event-store taken
;; together in a map with :read-model and :event-store keys.
;; A command fn is a function which takes the current state and
;; returns a seq of events to apply to the data model.
(defprotocol Command
(perform [this system] "Check validity, do side effects, and return events to apply to system."))
(extend-protocol Command
clojure.lang.IPersistentVector
(perform [this system state]
(doall (map #(perform % system state) this))))
(defn with-timestamp
[x]
(with-meta x {::timestamp (java.util.Date.)}))
(defn timestamp
[x]
(::timestamp (meta x)))
(defn do-command
"Apply a command to the system."
[{:keys [read-model event-store] :as system} command]
(let [raw-events (perform command system @read-model)
events (map with-timestamp raw-events)]
(dosync
(store event-store events)
(alter read-model (partial affect events)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment