Hi all,

(apologies for the wall of text but I think the context might be useful)

I am using event sourcing so the world, at any given point in time is 
simply a `reduce` over those events. Throughout the application different 
things are interested in different events. 

A design that is emerging is the notion of a 'world' which knows how to 
consume the various events and allows other parts of the system to respond 
to certain states having happened. All of this is in-memory and could be 
client or server side (yay for .cljc and ClojureScript).

In concrete terms imagine I have am modelling shopping baskets (again, all 
in memory). I might be interested in knowing:
 - whenever a certain item is added to a basket
 - whenever a basket is cancelled
 - whenever a complete basket is ordered

I could of course just filter the event log and pick out those events but I 
typically want the entire entity _as it was when that event happened_, so 
the event itself isn't sufficient.

My question is how best to model the 'I am interested in pre-x and post-y'. 
In general, it is interesting to know the event, the aggregate root 
(shopping basket) that event is associated with and the world (both the 
aggregate root and the world as they were at the time of the event).

I could have an EventObserver: (defprotocol EventObserver  (observe [this 
event entity world]) which the world notifies. One part of the system will 
have one set of EventObservers, another will have a different set of 
EventObservers. Also, some parts need to know _before_ the event and others 
_after_ the event.

I don't want each Observer to have to specify every single event so a 
protocol defining a pre/post method for each event wouldn't work because 
(AFAIK) you can't have a default implementation of a protocol and you can't 
have a partial implementation of a protocol. 

Where I am at is thinking that the world understands a map of 
EventObservers, with one key for each pre/post event:

{:pre-event-one EventObserver :post-event-one EventObserver
 :pre-event-two EventObserver :post-event-two EventObserver}

etc.

and each Observer can register their own map of EventObservers. I can 
optimise the code by either having the world handle nil EventObserver or 
having a default fully-populated map of EventObservers which Observers can 
simple assoc their own handlers onto. 

Building the world is then trivially (usual disclaimer - hacky 
stream-of-consciousness code):

(defn- locate-entity [world entity] ...)
(defn- update-entity! [world entity] ...)

(defn- process-event [{:keys [observers world] :as result} event]
  (let [pre-handler-kw (keyword (str 'pre-' (name (:event-type event))))
         post-handler-kw (keyword (str 'post-' (name (:event-type event)))
         pre-entity (locate-entity world event)
         new-world (update-entity world entity)
         post-entity (locate-entity new-world event]
    (do all (for [o observers
                      :let [pre-event-observer (pre-handler-kw o) 
post-event-observer (post-handler-kw o)]]
                 (when pre-event-observer (pre-event-observer event 
pre-entity world))
                 (when post-event-observer (post-event-observer event 
post-entity new-world))))
   (assoc result :world new-world))

(defn build-world [events observers]
  (reduce process-event {:world {} :observers observers} events))

The above code could be improved in a myriad of ways, but hopefully it is 
clear enough to highlight the problem: what mechanism is idiomatic in 
Clojure to implement the Observers where each Observer is interested in a 
subset of before and after a subset of events.

If you are thinking 'duh, this is obvious - use X' or 'what! that's not 
true of course you can do X with protocols' then yep, I have almost 
certainly overlooked something.

Finally - yeah, at times like this I really miss AOP.

Thanks for still reading :-)

Colin


-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to