Revisiting The Backbone Event Aggregator: Lessons Learned

It’s been a while since I originally talked about using an event aggregator in my Backbone applications. Since then, I’ve encapsulated the “vent” object in my Backbone.Marionette application and I’ve also realized that a lot of what I wrote originally is steeped in C# thinking. Specifically, the way I’m passing the vent object in as a dependency everywhere, gets really old and frustrating really fast. I’ve also read some interesting blog posts in recent months that take the basic idea of what I wrote and run in some interesting directions.

Dependency Injection: Bad Idea

This is the first of the obvious mistakes that I made my original blog post and implementation. Passing around an event aggregator as a dependency for various objects is just plain painful in JavaScript. There’s no real value in doing this, either. The few benefits that it may provide are greatly outweighed by the flexibility inherent in JavaScript and our ability to work around the problem that dependency injection solves.

You can see the obvious problems in the original code, pretty easily:

AddEditView = Backbone.View.extend({
  initialize: function(options){
    _.bindAll(this, "editMedication");
    options.vent.bind("editMedication", this.editMedication);
  },

  editMedication: function(medication){
    this.model = medication;
    this.render();
  }
});

MedicationView = Backbone.View.extend({
  events: {
    "click #edit": "editMedication"
  },

  initialize: function(options){
    this.vent = options.vent;
  },

  editMedication: function(){
    this.vent.trigger("editMedication", this.model);
  }
});

// initialize everything, and tie it all together
// with the event aggregator object: vent

var vent = _.extend({}, Backbone.Events);

var addEditView = new AddEditView({vent: vent});

medicationList.each(function(med){
  new MedicationView({model: med, vent: vent});
});

Having to pass the `vent` object to every single constructor gets frustrating, fast. It turns in to a real nightmare, though, when you want to trigger an event from an object that is 5 or 6 levels deep in an object graph. If this is the only place that needs to trigger an event, you still have to pass the aggregator around to all of the objects in the graph.

Solution: Application Level Aggregator

One of the solutions, and the easiest way to start using an event aggregator, is to attach the aggregator to your application’s namespace object. This becomes the application level event aggregator that any code in your application can attach to and trigger events from.

MyApp = {};
MyApp.vent = _.extend({}, Backbone.Events);

MyApp.vent.on("some:event", function(){
  alert("some event was fired!");
});

MyApp.vent.trigger("some:event");

I’ve baked this idea directly in to my Marionette.Application object. When you instantiate a Marionette.Application, you get the `vent` attribute with it.

MyApp = new Backbone.Marionette.Application();

MyApp.vent.on("some:event", function(){
  alert("some event was fired!");
});

MyApp.vent.trigger("some:event");

Solution: Sub-Module Aggregators

Along with the application level event aggregator, there are times when you’ll want to have some events tossed around within an application sub-module. When I run in to this need, I don’t rely on the application’s `vent`, instead I’ll build one specific to the module.

For example, if I were building an application module to manage users and I needed to run some events in between various parts of the user management screens, I might do it like this:

MyApp = new Backbone.Marionette.Application();

MyApp.UserManagement = (function(Backbone){
  var mgmt = {};
  var mgmtEvents = new Backbone.Marionette.EventAggregator();

  mgmt.UserListView = Backbone.View.extend({
    events: {
      "click .user": "userClicked"
    },

    userClicked: function(){
      var user = // get the user that was clicked
      mgmtEvents.trigger("user:selected", user);
    },

    // ...
  });

  mgmt.UserDetailView = Backbone.View.extend({
    // ...
  });

  mgmtEvents.on("user:selected", function(user){
    var view = new mgmt.UserDetailView({
      model: user
    });
    view.render();
    $("#detail").html(view.el);
  });

  return mgmt;
})(Backbone);

Note that I’m also showing the Marionette.EventAggregator object in this example. This object is a tiny bit more than the standard `_.extend({}, Backbone.Events);` that I typically use, but not by much.

I still have access to the application’s event aggregator, but now I also have access to an aggregator that is specific to the module. This lets me publish and subscribe to events that are local to the module. Other modules in the application are not able to see this aggregator, so they are not able to publish / subscribe with it. If I need this module to talk to other modules, then, I use the application’s aggregator.

 

Multiple Channels For Events

This idea comes from a blog post I read a while back that posits the idea of having multiple channels for events. Thinking back to my full-scale enterprise-service-bus development days, I can see the value in this. It allows you to have different subscribers on different channels, letting each of those channels act independently. But there’s a few problems with this in JavaScript that are solved in much more simple manners.

The Idea

The basic idea behind “channels” is that you can have a single event aggregator or message bus that allows publishers and subscribers to communicate with each other in a segmented manner.

Think about a CB-radio for a moment. You can turn the CB to various channels. When you talk through your CB on a given channel, other people that are listening to that channel will hear you. If you are listening to a specific channel, you will hear other people that are talking on that channel. If you are talking on Channel 1, though, and someone else is listening on Channel 2, they will not hear you.

The Problems

The same principle is often applied in service-bus architectures, where standing up multiple services buses for communication is expensive. But this doesn’t translate too well in to JavaScript – at least not with simple event aggregators like I’m using.

A simple use of an event aggregator that supports channels might look something like this:

MyApp.vent.on("myChannel", "myEvent", function(){
  // do stuff here
});

MyApp.vent.trigger("myChannel", "myEvent");

In this example, we’re standing up an aggregator and then subscribing to an even on a channel. We then trigger the event on that channel, and our handler is called. This seems easy enough and it shouldn’t take that much work to implement channels.

The real problem, though, is that the number of publishers and subscribers using this one event aggregator can quickly get out of hand. Every time we trigger an event through a channel, we have to filter the list of subscribers so that we only publish the event to those listening on the correct channel. This filtering is going to take time. If we have a large number of subscribers in our aggregator, and we only need to send the event to one of them, why should we have to sift through the rest of them?

There are some optimizations that we can put in place, of course. We could set up the “on” method so that when you specify what channel to listen on, it stores your handler in a collection of listeners specifically for that channel. Then when you trigger an event, the channel can be matched to the collection of subscribers more quickly.

The odd part about this, though, is that we’re adding a lot of infrastructure and code to support what amounts to multiple event aggregators. We’re aggregating the aggregators and then using code to split apart the aggregate when we need to publish and subscribe.

Again, I understand why this seems like a good idea. When you’re dealing with actual network communications and distributed systems where a service bus is an expensive thing to stand up, you’ll want to make this kind of optimization. But when you’re dealing with an in-memory application and event aggregator, you’re adding overhead that is not needed. The easier option, instead, is to use multiple event aggregators: one per “channel”.

A Simple Solution: Many Event Aggregators

By setting up multiple event aggregators, we can more effectively optimize the memory and performance of our application.

MyApp.myChannel = _.extend({}, Backbone.Events);
MyApp.anotherChannel = _.extend({}, Backbone.Events);

MyApp.myChannel.on("someEvent", function(){
  // ...
});

MyApp.myChannel.on("anotherEvent", function(){
  // ...
});

Now when we have many overall subscribers to many event aggregators, publishing to a single event aggregator doesn’t have to think about which subscribers should and should not be checked, based on channels. When you publish to a specific event aggregator, it checks all of the subscribers that it has registered, and that’s it.

A Built In Solution: Event Namspacing

The other option that we see in Backbone events is the use of namespaced events. For example, when you set some data on a Backbone model, you get multiple events:

MyApp.vent.on("some:namespaced:event", function(){
  // ...
});

MyApp.vent.trigger("some:namespaced:event");

Now this isn’t truly a namespace in the event handler. There is no parsing of the event name to try and figure out where the : is in the event name, to filter out which subscribers should be receiving the event. This is only a convention that helps us, as developers, see which events belong together.

But Events Will Be Filtered Anyways

In the end, the event aggregator object does have to filter out the subscribers that don’t care about the event being triggered. There’s simply no way around this. When you have an event subscriber, you tell it what specific event to listen to. If a different event is triggered, that subscriber doesn’t get called. This is filtering by it’s nature and we can’t reasonably get away from that. My argument, though, is that we should limit the filtering as much as possible because it’s more expensive to filter a larger list than it is to split the list out in to multiple, separate objects.

 

Semantics: Separating Command Messages From Event Messages

This is a topic that I tried to explore already, but I never found a good answer. I’m still looking for a good way to separate the idea of a command system from an event system in JavaScript. Both of these are message-based patterns, but I think the semantics of the message types is very important. I don’t want to see my command messages being passed around with the “on” / “off” / “trigger” method semantics. It doesn’t fit and it can get confusing which will lead to bugs in a system.

Client-Side Messaging Anti-Patterns

Jim Cowart has a great article on client side messaging anti-patterns. I’m not going to try and re-visit any more items in his list, as this post is getting long enough already. Instead, I would highly recommend reading his post. I’m not sure I 100% agree with every last detail, but I think this largely comes down to opinions and not real technical merit. I’m probably close to 99.9% agreement, though.

A Better Way Forward

Hopefully this post will help clear up some of the confusion and problems that I caused in my original post, while also presenting some ideas that can help you build a better organization around your event aggregator usage. I’m sure this won’t be the last set of lessons I learn or opinions I form on the subject, though. As always, I reserve the right to realize the mistakes I’ve made and change my opinion, without warning.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Backbone, Composite Apps, Javascript, Marionette, Messaging. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.cornerstonenw.com/ Peter Rust

    Thank you Derick — good post, as always.

    I wholeheartedly agree about multiple event aggregators vs. channels. I’m not convinced that the memory/performance differences are significant, but the most important thing to me is that it is more explicit. With just a single global event aggregator, any component can publish or subscribe to any channel. With separate aggregators, the dev is forced to pass in the relevant one(s) and consider whether the component *should* have a dependency on the aggregator(s) in question.

    It’s similar to having every component readily available as a global… it can be too easy to do things wrong. But if you have to pass in which aggregators a component should have access to, it forces you to think twice — it’s becomes less work to do things right (locally, within the component) and more work to do things wrong (break encap, publishing/subscribing to channels it shouldn’t pub/sub to).

    Regarding command messages, I much prefer to just use method calls wherever possible. Of course there are situations where they are necessary (chain of responsibility pattern, command pattern, etc) — but so far the only time I’ve used them was to provide the foundation for undo/redo functionality, which I never got around to implementing.

    Event messages are a good way to decouple components that aren’t genuinely dependent on each-other, but in the case of commands… my first inclination is that a command betrays a genuine dependency and that the dependent component should be passed in as such.

    Regarding the annoyance of passing in a dependency everywhere, check out Where Have All The New Operators Gone (http://misko.hevery.com/2008/09/10/where-have-all-the-new-operators-gone/), linked to from Google Testing Blog (http://googletesting.blogspot.com/2008/10/dependency-injection-myth-reference.html).

    I was surprised by the statement “The few benefits that [dependency injection] may provide are greatly outweighed by the flexibility inherent in JavaScript and our ability to work around the problem that dependency injection solves.” JS is indeed a very flexible language and at our shop we regularly work around the lack of dependency injection in our codebase (and the corresponding preponderance of globals) with Jasmin Spies on globals. But I see that as a stop-gap survival technique; not the right solution. For new code that I write, I try to make the dependencies clear and to solve the pain of needing to always pass dependencies via factories. As Uncle Bob argues in Clean Code (ch11, http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) it boils down to Separation of Concerns. For a very simple system with few dependencies, it’s fine to just use the “new” operator and pass dependencies where they’re needed. But as a system grows more complex, as the dependencies grow and things are refactored to be more fine-grained, it makes sense to consolidate the concern of construction — what dependencies are required and in what order — into a factory.

    • http://mutedsolutions.com Derick Bailey

      Hey Peter,

      Good point about memory vs doing what’s right, etc. I was a little iffy when i wrote that part about memory, but kept it in anyways.

      And thanks for the tremendous info and set of links and resources! There’s definitely a lot that I need to dig through. 

      I generally agree with the statements about DI in JS, though. I find very little need for this, from an architectural / structural level. Of course, I still pass dependencies around for individual method calls, but the idea of an IoC container and some of the other hoops and tricks and jumps we have to go through with a language like C# make me cringe in the JS world. :)

      I definitely like the idea of factories, too. Even if an IoC container is just a mega-meta-uber-factory, keeping simple factories around for specific purposes is usually a good way to keep code clean.

  • Anonymous

    Thanks for this great write-up, Derick!  

    I plan on using the “global” aggregator for app-wide events.  I have a different idea for sub-module aggregators, however, since in my case I have a multi-tabbed application where several tabs might be backed by different instances of the same view.  In that case, I can’t just hang sub-module aggregators off the global namespace, since each instance of the view should have its own aggregator.

    What I’m thinking of is a kind of “aggregator dispatcher”.  Stay with me here… it’s not as bad as it sounds. :)

    I’m thinking of this like someone who’s driving around lost, and has an emergency.  They don’t know where they are, and they don’t know which local police department to call, so they just call 911 and get routed to the proper operator based on their location.  

    Similarly, the component wishing to dispatch an event doesn’t know which module it belongs to, so it just calls a central dispatcher which looks up the proper “vent” based on the view’s parent (recursively traversed until the view which registered its own vent is matched).

    // 911dispatcher.js

    MyApp.registeredVents = { };

    MyApp.dispatchToProperVent = function(source, event) {

        if (MyApp.registeredVents[source.attr(id)]){
            MyApp.registeredVents[source.attr(id)].trigger(event);
        } else if(source.parent() {
            MyApp.dispatchToProperVent(source.parent());
        }

    }

    // some-parent-view.js

    return new Backbone.View.extend({
         var myVent = Backbone.Marionette.EventAggregator();
         MyApp.registerVent($el, myVent);
    });

    // some-child-view.js (child view of some-parent-view.js)

    MyApp.dispatchToProperVent($el, someEvent);

    For very deeply-nested sub-views, I could see this being a performance issue.  But it would allow dynamic views & subviews to share events within dynamic aggregators without having to hang aggregators off a statically-named namespace.

    Am I totally crazy?  Or could this possibly work?

  • http://truffles.me.uk Tim Ruffles

    Have been researching Marionette today and it’s full of interesting ideas – thanks for your hard work on it!

    I was interested in how you test your apps based upon it though? Looking at the example code, there seems to be a lot of reliance on globals.

    I’ve pasted a good example below – how would you test this?

    https://github.com/derickbailey/bbclonemail/blob/master/public/javascripts/contacts/bbclonemail.contactsapp.js#L34

    You’re relying on two other modules setting up some global state. I understand using a global for vent as a tradeoff (though I might go for sth like https://gist.github.com/3047279), but I can’t see how code like the above avoids the common issues with global vars? Namely, for testing, having to mock all the globals then tear them down, needing quite a large graph of objects (BBCM.CA.CL, rather than passing only the required objects for each test), etc.

    • gumaflux

      Hey Tim, just saw this comment – which covers some issues I am having at the moment trying to work TDD with Backbone Marionette. How did you proceed and do you have any gotchas to share with regards to mocking the Event Aggregator and similar objects.

      • http://truffles.me.uk Tim Ruffles

        I normally end up either:

        a) injecting all dependencies via constructor args, extending View to take a `vent` from options

        b) injecting in tests, and letting it fall back to a global in production, but with the option to over-ride via constructor args if desired: https://gist.github.com/timruffles/3047279

        • gumaflux

          Great stuff! Appreciate the input. Will try and see which fits better for my scenarios.

  • Eric Barr

    Big fan — you’re articles are great. I’m in total agreement that an event aggregator (EA) is appropriate for any backbone app beyond ‘ToDo’.

    Looking at the above, however, it seems that the sample goes beyond being an EA. My gut tells me that *vent* will become a dumping ground for logic. Fowler describes a simple EA as “aggregat[ing] events from multiple objects into itself, passing that same event onto its observers.” It’s the ‘passing it on’ or ‘listening’ portion that seems to be missing.

    Wouldn’t you get the same effect, but without the potential logic clutter, by doing something like the following:
    * creating a global model, one field per ‘channel’
    * Observers use backbone’s built in event infrastructure and subscribe to changes on the applicable attribute.
    * Objects that want to issue events can simply update the appropriate global model’s field.

    This structure would prevent anything but the simplest events from being passed (without additional work). Although phrased in terms of a global model, the structure is probably 50% serivce bus, and 50% EA.

    Thoughts?

    • http://mutedsolutions.com Derick Bailey

      +1 to everything you’re saying about what an EA should be doing. Using a model for the EA would only change the implementation and wouldn’t prevent a developer from doing things they shouldn’t do, though.

      There are a few problems with using a model for the EA, too. The introduction of unwanted overhead, for example. There’s far more code that gets run in a model than there is in the basic EA that I use. And the destruction of important semantics, by using a model, is also a very bad idea. Semantics are important, and using a model as an EA will lead to confusion at some point, either when trying to use it or when trying to make it do something it shouldn’t do.

      Backbone’s built in event system is what I use for my event aggregators:
      var vent = _.extend({}, Backbone.Events);
      Backbone’s models use the same Backbone.Events object in the same manner. I’m just creating an object with less overhead, for a specific purpose, and with a more appropriate name to cut down on semantic confusion for the people reading and writing the code.

      • Eric Barr

        +1 for speed — what was that a 9 minute response time?

        Good point about the infrastructure overhead from a model vs. an
        event. Although, in most scenarios the volume of events is low enough to make this a minor point.

        The stronger point you make is that semantics are important. You’re right that using a global model destroys those semantics — point taken.

        SUBSCRIPTIONS
        This brings us back to my point on subscribing to *vent*. The post suggests that when creating the *vent*, the callback is where you should “do stuff”. I suspect you were thinking about observing/subscribing, but the code snippets push in a different direction.

        If it were me, i would change “1.js” to read more like (attached image) or : https://gist.github.com/3230913#file_1.js

        This way the example shows triggering & observing.

        • http://mutedsolutions.com Derick Bailey

          right… we’re saying the same thing, then. :)

          i just showed a really poor example, because … well… it’s an example :D

  • http://robert-agthe.de Robert Agthe

    Thanks for your post Derick, very helpful. Speaks something against jQuerys “custom events” as an Event Aggregator? As far as i know is it more or less the same thing. And because backbone relies already on jquery, an additional event aggregator should become obsolete.

    • http://mutedsolutions.com Derick Bailey

      why does backbone provide it’s own event system if jquery’s would work good enough? partially because jquery isn’t a requirement. zepto and others can be used. you can also leave DOM manipulation out of your backbone apps entirely. you just can’t use Backbone.view.

      also, semantics and separation of concerns are important. i don’t want my event aggregator to be associated with or require a DOM manipulation library because event aggregation has nothing to do with DOM manipulation.

      but there’s no reason you can’t use jquery for your event aggregator in your apps.

  • http://twitter.com/Kerry350 Kerry Gallagher

    Great post as always Derick, your posts have really helped me get to grips with Backbone.js over the past couple of months – thank you, but I was wondering if I could just pick your brains on something…

    I’ve been implementing your idea of an event aggregator, but also much of the advice that you give on Zombie views and proper cleanup. How do you deal with cleanup code not accidentally unbinding global event aggregator events for other views? I’ll elaborate, because I know that makes no sense on it’s own!

    Say you hit a route, this route passes off to a controller method that instantiates some views, models etc – standard stuff, lastly the controller calls the app level showView method to swap in the new view, and clean up the old one. So the following happens:

    - When the controller method is called View A is instantiated, View A makes some bindings to the global event aggregator.

    - The last part of the controller method is calling showView, showView calls close on View B, who had some bindings to the same type of event that A is now bound to. So as part of View B’s cleanup all of it’s bindings to the global event aggregator are unbound.

    - View B’s cleanup includes, for example, something like App.vent.off(“thing:clicked”, this.openMe);

    - As this is a global, View A’s newly bound events are also unbound at the same time.

    Should I just not be making event aggregator bindings in my ‘top-level’ view? (e.g. View A renders in sub-views, they’re okay, events unaffected, as they’re not actually instantiated until render() is called on view A as the last part of showView)

    Or should I be namespacing these events or something similar?

    Sorry that took so long to explain, and many thanks if you can offer any help :)

    • http://mutedsolutions.com Derick Bailey

      Hi Kerry – I’m sorry I haven’t responded until now. It’s been a rather busy few weeks for me.

      For handling global events with an event binder, and needing to clean up just the ones that were for the specific part of the app that I’m in, I just create my own instance of an event binder and add only the events that I need at that point in time. Then I can “unbindAll” on that event binder instance when I need to, and not worry about destroying all of the app level events.

      you can see an example of this in the wiki here: https://github.com/marionettejs/backbone.marionette/wiki/When-to-use-the-EventBinder

      Hope that helps. If you have any more questions about this, it would be best to ask on the google group for marionette: https://groups.google.com/forum/#!forum/backbone-marionette

      • http://twitter.com/Kerry350 Kerry Gallagher

        Thanks Derick, no need to apologise at all :)

        That makes perfect sense – I realise now at the time I posted the comment I was also unaware of the third argument in .off, the context, which also answers my own question. Backbone.js has really made me learn about things like events (which you almost take for granted when doing JS development), object lifespan etc in depth – which can only be a good thing, but also kinda scary when things you thought you knew…you really don’t. But, posts like yours have really helped me along the way, so thank you :)

  • Jonathan Cipriano

    Nice post. First time to your blog. I don’t use Marionette, but I’ll have to check it out.

    I’ve been toying with the idea of extending backbone to include automated dependency injection, but could not convince myself of the advantages. Like you, I was referring to my experience in C# (WPF) as well as Actionscript (Flash).

    Currently I use the approach you describe here by attaching an event bus to the application namespace. I do the same with a model that needs to be accessed from multiple areas of the app. It “feels” bad to do, but after reading your post I’m somewhat convinced to stick with it. Anything else seems to overcomplicate things as it tries to deal with hurdles that just do not exist in Javascript.

    Some posts below mention that this approach could lead to a “dumping ground”. Totally true, but Javascript itself is one huge dumping ground already. It’s really up to the developers to discipline themselves.

  • mxdubois

    Your article inspired me to put together a little factory method for creating/getting namespaced event channels on the fly. You can check it out here: https://github.com/mxdubois/backbone-event-channels

  • http://www.facebook.com/thoughtstrailoff Evan Hobbs

    This has become one of the most useful Backbone patterns I’ve learned. Thanks! Before I was using all kinds of weird logic to maintain seperation concerns while still allowing communication between objects. One thing I’d point out is the event object is already added to the Backbone object for just this purpose. It’s not in the docs but it’s in the code for backbone.js:

    // Allow the `Backbone` object to serve as a global event bus, for folks who
    // want global “pubsub” in a convenient place.
    _.extend(Backbone, Events);

    So for smaller apps you can just call Backbone.on(‘event’) and Backbone.trigger(‘event’) without needing to pass around the ‘Vent’

  • Erich Schulz

    For utter noobs like me trying to figure out how to pass parameters to events: just put them as second parameter in your trigger call.

  • peaceLion

    I’m new to Marionette and can’t get my head around events…

    I have an item which trigger an event, and I would like to receive this event at the application level, but when my app listen for this event, nothing happens…

    If the Event Aggregator is in Application and ItemView, why this code doesn’t work ? :

    var MessageItemView = Backbone.Marionette.ItemView.extend({
    template: “#messagesTPL”,
    tagName: ‘tr’,
    className: ‘messageItem’,
    events : {
    ‘click’: ‘displayMessage’
    },
    displayMessage: function () {
    this.trigger(‘display:message’);
    }
    });

    App.on(‘display:message’, function () {
    console.log(‘display message !!!’);
    });

    • Rajan Chawla

      You need to do:

      MessageItemView.on(‘display:message’, function () {
      console.log(‘display message !!!’);
      });