Thanks for the link Chris. I've actually read over this wiki link before, but didn't think about it too hard. I was hoping I wouldn't have to make drastic changes to the infrastructure to handle my particular use, but it seems that might be the case. If I'm going to essentially write my own messaging bus that handles dynamic subscriptions and many to many handlers, I should probably just not bother with re-frame. The other quirk that kind of bothers me is how each handler defines its own middleware. I tend to think of middleware as an application-wide orthogonal offering for logging, debugging, reporting, etc. I don't like the idea of having to manually insert middleware for each and every handler, even if I generate a middleware factory.
--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to the Google Groups "ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.
To post to this group, send email to clojur...@googlegroups.com.
Visit this group at https://groups.google.com/group/clojurescript.
I can imagine a convenient queuing system that captures everyone who wants to make a change, attempts the change, and throws if two changes attempt to modify the same piece of state... or some sort of explicit priority system (yuck).
I wonder if there is a better way to achieve my end-goal through a different means that's more in-line with the re-frame philosophy?
In my naivete I would attempt solving the problem this way:
[:button-clicked] event
top-level handler captures event, sets state [:clicked true]
component A, B both detect [:clicked ] flag change, do some dispatch of their own?
A, B handlers capture their respective dispatches
This seems awful for a few reasons. Namely the "circular dispatching" that make things really hard to debug/reason about.
Might you suggest another way? I could conceive of a "top level" component holding onto A and B and being the coordinator between the two... when [:button-clicked] shows up, it knows about A and B and how to get the data for both. The problem with this approach is it's not very modular, and doesn't scale well. How could you have a handful of A and B components smattered about, or a handful of top-level components, for that matter? Get's ugly quick.
I thought of a parallel solution to this, but I think I like yours better:
Dispatches all go into a channel, and components register to this channel. They get to say "on [:some-event], also dispatch [:my-other-event], [:another-event]". When a dispatch happens, it gathers up all registered events and does a dispatch for each event, including the original.
Essentially the same Idea, but not an independent "signals channel" versus "events channel".
How did that work out for you? Any pain-points trying to trace down [:some-event] -> ack what is happening here, 4 more thingies just happened and I don't know why?
Hi Mike,
That very well could be my problem. 2 years ago I wrote a custom event-driven architecture to serve a very data heavy application with some other special needs related to event sourcing. I'm also ~6 months into a redux-style architecture for another non-trivial workflow app. Maybe I just don't "get it", or maybe I'm not willing to "let go and trust the system".
I have a huge hang-up around modularity, having been bitten in the past. Architectures such as redux really don't solve that modularity problem out of the box, and I suspect (correctly or not) re-frame might suffer the same fate. To me, message-passing at the top-most level between components (in the DDD sense, aggregate roots) is "the way" to maintain decoupling and reuse. I get nervous when I don't see any strategy for communicating between two completely independent pieces through a well-defined API (message passing or others).
With respect to this concern, I wrote up a quick gh issue that may or may not prove helpful to re-frame: https://github.com/Day8/re-frame/issues/160
I'm glad to hear it worked out well for you. I am currently working with a large redux application where we had to use sagas to coordinate independent components. The sagas work wonderfully (generators and yields, like core.async, are great mental models), but it does break a lot of the debugging tools because you have now given up 100% "pure 1-to-1 this happened then that happened". Generators/sagas/core.async hold onto state internally, so it's a pretty big sacrifice to give up wholly serializable state.
If redux has proved a poor fit for your app, then you are probably right to be wary of re-frame, or at least its reference implementation.
If your app is "workflow" related, I'm imagining "plugins", hence your discussion around modules.
Perhaps you should consider taking the re-frame reference impl and tweaking it into the shape you want (multiple handlers). Normally, you'd be completely mad to invent your own framework, right? But there's really not many lines of code in re-frame (200?) and something like multiple-handlers would probably require a change to about 10 of them.
--
Mike
Thanks for that issue, I'll cycle back around and address it fully in due course. But more quickly I can easily answer your question on middleware:
> middleware - this isn't a weakness of re-frame, more just a design decision, > but I would be interested to hear how people feel about having to attach
> middleware to each handler independently? I have always thought middleware
> were orthogonal, app-wide wrappers initialized once and forgotten. I find it > strange that re-frame has a per-handler middleware scenario.
It is very easy to add middleware to all your handlers in one fell swoop. But we find it useful to add middleware selectively. For example, not every handler involves an undoable action.
But if you did want a global set of middleware (eg undoable and denug?) added to all handlers you'd do this:
(defn my-register-handler
([id handler]
(re-frame.core/register-handler id [(undoable) debug] handler)))
And, from then on, you'd just use my-register-hander to register all your handlers. That would always give you undoable and debug on all your handlers.
In fact, you'd probably write a multi-arity version of my-register-handler so you could selectively add further middleware to the global middleware set like this:
(defn my-register-handler
([id handler]
(re-frame.core/register-handler id nil handler))
([id extra-middleware handler]
(re-frame.core/register-handler id [(undoable) debug extra-middleware] handler)))
--
Mike
Yes, indeed, it isn't monkey patching at all. re-frame was designed specifically for this to happen. Notice how you can supply vectors of middleware with arb nesting, to allow for middleware composition in this way.
And, sometimes we also have an app specific "dispatch" too. We generally name it "emit", and this function can do whatever you want before eventually calling "dispatch". If you needed it to, "emit" can add an entirely different level of routing, including the ability to "look something up" based on the event supplied, and then dispatch twice or three times based on the results of that lookup - I'm thinking of module "a" and module "b" from your original post.
So it is all pretty flexible as-is.
--
Mike
I see, thanks for that. I guess I was avoiding that strategy initially because it sort of felt like monkey patching in ruby/js, which is just bound to break eventually. It's not monkey-patching in this respect, though, it's just app-specific api design.
--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/qIvYyk5Ptek/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.