Hiya,

I did consider (and have used) guarded visitors before but I discounted them 
because ‘action!’ ended up having the `case` statement when that visitor was 
interested in multiple events. I guess I could have one visitor per event ….

To be specific, these visitors are pure, read-only visitors - they won’t mutate 
the world they only react to specific changes in the world.

Interesting - thanks Aaron.

> On 8 Dec 2015, at 01:32, Aaron D. Valade <aval...@gmail.com> wrote:
> 
> What I’ve done in a similar situation is having a protocol defined like so:
> 
> (defprotocol Transition
>   (valid? [this world input] “Checks if the transition is valid for this 
> input given the state of the world”)
>   (action! [this world input] “Changes the state of the world based upon the 
> input and returns a new world state”)
> Then you can define a runner:
> 
> (defn check-transitions
>   [transitions]
>   (fn [world input]
>     (let [run-t (fn [world t]
>                   (if (valid? t world input)
>                     (action! t world input)
>                     world))]
>       (reduce run-t world transitions)))
> Now, just implement your transitions and then for each input you receive, run 
> it through the check-transitions function. The =valid?= predicate function is 
> called to check if the transition is interested in the input event and then 
> we only call =action!= when needed. By running a reduce, we then iterate over 
> the transitions sequentially, passing the new world state to each transition.
> 
> You could also wrap the =world= item in an atom and then change the 
> =check-transitions= into a transducer that could be the xform for a channel 
> to hook up to whatever is generating your inputs. Or even just leave the 
> =world= item as local state in a closure of the xform and just emit the new 
> worlds from the other end of the channel.
> 
> On 8 Dec 2015, at 1:19, Colin Yates wrote:
> 
> Thanks Jason,
> 
> I don’t particularly want dynamic registration; when the ‘world’ is 
> instantiated it can now about the observers.
> 
> I could do this but it is missing the ‘filter out uninteresting events’ bit. 
> I want each observer to declare its interest.
> 
> Your ‘middleware pattern’ however is something I would use to do the 
> delegation (i.e. the actual glue that pushes to each interested observer) as 
> the world itself shouldn’t really care.
> 
> Thanks for the thoughts - keep them coming!
> 
> On 7 Dec 2015, at 17:15, Jason Felice jason.m.fel...@gmail.com 
> <mailto:jason.m.fel...@gmail.com> wrote:
> 
> It looks like you want dynamic registration of event handlers, which is not 
> something I've done. If you didn't want that, then this the middleware 
> pattern:
> 
> (defn null-processor
> [world event]
> world)
> 
> (defn some-other-middleware
> handler 
> <x-msg://17/fn%20%5Bworld%20event%5D%0A%20%20...%0A%20%20(handler%20world%20event>
> ...
> ) => world'
> 
> (def processor
> (-> root-middleware
> some-other-middleware
> ...))
> 
> Each processor can respond to some subset of events and ignore the rest. In 
> this case I folded "basket" into world.
> 
> I've thought a bit about making data-driven middleware and how to register or 
> deregister, but not come up with a decent solution – mostly because ordering 
> is often important.
> 
> On Mon, Dec 7, 2015 at 6:01 AM, Colin Yates <colin.ya...@gmail.com 
> <mailto:colin.ya...@gmail.com>> wrote:
> 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 
> <x-msg://17/let%20%5Bpre-handler-kw%20(keyword%20(str%20'pre-'%20(name%20(:event-type%20event>)))
> 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)] 
> <x-msg://17/when%20pre-event-observer%20(pre-event-observer%20event%20pre-entity%20world>)
> (when post-event-observer (post-event-observer event post-entity new-world))))
> (assoc result :world new-world))
> 
> (defn build-world events observers 
> <x-msg://17/reduce%20process-event%20%7B:world%20%7B%7D%20:observers%20observers%7D%20events>)
> 
> 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 
> <mailto:clojure@googlegroups.com> clojure@googlegroups.com 
> <mailto: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 
> <mailto:clojure+unsubscr...@googlegroups.com> 
> clojure%2bunsubscr...@googlegroups.com 
> <mailto:clojure%2bunsubscr...@googlegroups.com>
> For more options, visit this group at
> 
> http://groups.google.com/group/clojure?hl=en 
> <http://groups.google.com/group/clojure?hl=en> 
> http://groups.google.com/group/clojure?hl=en 
> <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 
> <mailto:clojure+unsubscr...@googlegroups.com> 
> clojure+unsubscr...@googlegroups.com 
> <mailto:clojure+unsubscr...@googlegroups.com>.
> For more options, visit https://groups.google.com/d/optout 
> <https://groups.google.com/d/optout> https://groups.google.com/d/optout 
> <https://groups.google.com/d/optout>.
> 
> -- 
> 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 
> <mailto: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 
> <mailto:clojure+unsubscr...@googlegroups.com>
> For more options, visit this group at
> 
> http://groups.google.com/group/clojure?hl=en 
> <http://groups.google.com/group/clojure?hl=en> 
> http://groups.google.com/group/clojure?hl=en 
> <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 
> <mailto:clojure+unsubscr...@googlegroups.com> 
> clojure+unsubscr...@googlegroups.com 
> <mailto:clojure+unsubscr...@googlegroups.com>.
> For more options, visit https://groups.google.com/d/optout 
> <https://groups.google.com/d/optout> https://groups.google.com/d/optout 
> <https://groups.google.com/d/optout>.
> 
> -- 
> 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 
> <mailto: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 
> <mailto:clojure+unsubscr...@googlegroups.com>
> For more options, visit this group at
> 
> http://groups.google.com/group/clojure?hl=en 
> <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 
> <https://groups.google.com/d/optout>.
> 
> 
> -- 
> 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 
> <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 
> <mailto:clojure+unsubscr...@googlegroups.com>.
> For more options, visit https://groups.google.com/d/optout 
> <https://groups.google.com/d/optout>.

-- 
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