Event Aggregator And/Or/vs Mediator: A Tale Of Two Patterns

Design patterns often differ only in semantics and intent. That is, the language used to describe the pattern is what sets it apart, more than an implementation of that specific pattern. It often comes down to squares vs rectangles vs polygons. You can create the same end result with all three, given the constraints of a square are still met – or you can use polygons to create an infinitely larger and more complex set of things.

When it comes to the Mediator and Event Aggregator patterns, there are some times where it may look like the patterns are interchangeable due to implementation similarities. However, the semantics and intent of these patterns are very different. And even if the implementations both use some of the same core constructs, I believe there is a distinct difference between them. I also believe they should not be interchanged or confused in communication because of the differences.

It’s All About Logic

The TL;DR version of this article is this: where does the logic live? An event aggregator has no logic, other than forwarding events from a publisher to a subscriber. A mediator, on the other hand, encapsulates the potentially complex logic of coordinating multiple other objects and/or services, to accomplish a goal. A mediator contains real application / business / workflow / process logic.

This is the line I’m drawing in the ever-shifting sands of implementation details and fuzzy heuristics.

Event Aggregator

The core idea of the Event Aggregator, accoring to Martin Fowler, is to channel multiple event sources through a single object so that other objects needing to subscribe to the events don’t need to know about every event source.

Backbone’s Event Aggregator

The easiest event aggregator to show is that of Backbone.js – it’s built in to the Backbone object directly.

var View1 = Backbone.View.extend({
  // ...

  events: {
    "click .foo": "doIt"
  },

  doIt: function(){
    // trigger an event through the event aggregator
    Backbone.trigger("some:event");
  }
});

var View2 = Backbone.View.extend({
  // ...

  initialize: function(){
    // subscribe to the event aggregator's event
    Backbone.on("some:event", this.doStuff, this);
  },

  doStuff: function(){
    // ...
  }
})

In this example, the first view is triggering an event when a DOM element is clicked. The event is triggered through Backbone’s built-in event aggregator – the Backbone object. Of course, it’s trivial to create your own event aggregator in Backbone, and there are some key things that we need to keep in mind when using an event aggregator, to keep our code simple.

jQuery’s Event Aggregator

Did you know that jQuery has a built-in event aggregator? They don’t call it this, but it’s in there and it’s scoped to DOM events. It also happens to look like Backbone’s event aggregator:

$("#mainArticle").on("click", function(e){

  // handle the event that any element underneath of our #mainArticle element

});

This code sets up an event handler function that waits for an unknown number of event sources to trigger a “click” event, and it allows any number of listeners to attach to the events of those event publishers. jQuery just happens to scope this event aggregator to the DOM.

Mediator

A Mediator is an object that coordinates interactions (logic and behavior) between multiple objects. It makes decisions on when to call which objects, based on the actions (or in-action) of other objects and input.

A Mediator For Backbone

Backbone doesn’t have the idea of a mediator built in to it like a lot of other MV* frameworks do. But that doesn’t mean you can’t write one in 1 line of code:

var mediator = {};

Yes, of course this is just an object literal in JavaScript. Once again, we’re talking about semantics here. The purpose of the mediator is to control the workflow between objects and we really don’t need anything more than an object literal to do this.

var orgChart = {

  addNewEmployee: function(){

    // getEmployeeDetail provides a view that users interact with
    var employeeDetail = this.getEmployeeDetail();

    // when the employee detail is complete, the mediator (the 'orgchart' object)
    // decides what should happen next
    employeeDetail.on("complete", function(employee){

      // set up additional objects that have additional events, which are used
      // by the mediator to do additional things
      var managerSelector = this.selectManager(employee);
      managerSelector.on("save", function(employee){
        employee.save();
      });

    });
  },

  // ...
}

This example shows a very basic implementation of a mediator object with Backbone based objects that can trigger and subscribe to events. I’ve often referred to this type of object as a “workflow” object in the past, but the truth is that it is a mediator. It is an object that handles the workflow between many other objects, aggregating the responsibility of that workflow knowledge in to a single object. The result is workflow that is easier to understand and maintain.

Similarities And Differences

There are, without a doubt, similarities between the event aggregator and mediator examples that I’ve shown here. The similarities boil down to two primary items: events and third-party objects. These differences are superficial at best, though. When we dig in to the intent of the pattern and see that the implementations can be dramatically different, the nature of the patterns become more apparent.

Events

Both the event aggregator and mediator use events, in the above examples. An event aggregator obviously deals with events – it’s in the name after all. The mediator only uses events because it makes life easy when dealing with Backbone, though. There is nothing that says a mediator must be built with events. You can build a mediator with callback methods, by handing the mediator reference to the child object, or by any of a number of other means.

The difference, then, is why these two patterns are both using events. The event aggregator, as a pattern, is designed to deal with events. The mediator, though, only uses them because it’s convenient.

Third-Party Objects

Both the event aggregator and mediator, by design, use a third-party object to facilitate things. The event aggregator itself is a third-party to the event publisher and the event subscriber. It acts as a central hub for events to pass through. The mediator is also a thirdy party to other objects, though. So where is the difference? Why don’t we call an event aggregator a mediator? The answer largely comes down to where the application logic and workflow is coded.

In the case of an event aggregator, the third party object is there only to facilitate the pass-through of events from an unknown number of sources to an unknown number of handlers. All workflow and business logic that needs to be kicked off is put directly in to the the object that triggers the events and the objects that handle the events.

In the case of the mediator, though, the business logic and workflow is aggregated in to the mediator itself. The mediator decides when an object should have it’s methods called and attributes updated based on factors that the mediator knows about. It encapsulates the workflow and process, coordinating multiple objects to produce the desired system behaviour. The individual objects involved in this workflow each know how to perform their own task. But it’s the mediator that tells the objects when to perform the tasks by making decisions at a higher level than the individual objects.

An event aggregator facilitates a “fire and forget” model of communication. The object triggering the event doesn’t care if there are any subscribers. It just fires the event and moves on. A mediator, though, might use events to make decisions, but it is definitely not “fire and forget”. A mediator pays attention to a known set of input or activities so that it can facilitate and coordinate additional behavior with a known set of actors (objects).

Relationships: When To Use Which

Understanding the similarities and differences between an event aggregator and mediator is important for semantic reasons. It’s equally as important to understand when to use which pattern, though. The basic semantics and intent of the patterns does inform the question of when, but actual experience in using the patterns will help you understand the more subtle points and nuanced decisions that have to be made.

Event Aggregator Use

In general, an event aggregator is uses when you either have too many objects to listen to directly, or you have objects that are unrelated entirely.

When two objects have a direct relationship already – say, a parent view and child view – then there might be little benefit in using an event aggregator. Have the child view trigger an event and the parent view can handle the event. This is most commonly seen in Backbone’s Collection and Model, where all Model events are bubbled up to and through it’s parent Collection. A Collection often uses model events to modify the state of itself or other models. Handling “selected” items in a collection is a good example of this.

jQuery’s on method as an event aggregator is a great example of too many objects to listen to. If you have 10, 20 or 200 DOM elements that can trigger a “click” event, it might be a bad idea to set up a listener on all of them individually. This could quickly deteriorate performance of the application and user experience. Instead, using jQuery’s on method allows us to aggregate all of the events and reduce the overhead of 10, 20, or 200 event handlers down to 1.

Indirect relationships are also a great time to use event aggregators. In Backbone applications, it is very common to have multiple view objects that need to communicate, but have no direct relationship. For example, a menu system might have a view that handles the menu item clicks. But we don’t want the menu to be direcly tied to the content views that show all of the details and information when a menu item is clicked. Having the content and menu coupled together would make the code very difficult to maintain, in the long run. Instead, we can use an event aggregator to trigger “menu:click:foo” events, and have a “foo” object handle the click event to show it’s content on the screen.

Mediator Use

A mediator is best applied when two or more objects have an indirect working relationship, and business logic or workflow needs to dictate the interactions and coordination of these objects.

A wizard interface is a good example of this, as shown with the “orgChart” example, above. There are multiple views that facilitate the entire workflow of the wizard. Rather than tightly coupling the view together by having them reference each other directly, we can decouple them and more explicitly model the workflow between them by introducing a mediator.

The mediator extracts the workflow from the implementation details and creates a more natural abstraction at a higher level, showing us at a much faster glance what that workflow is. We no longer have to dig in to the details of each view in the workflow, to see what the workflow actually is.

Event Aggregator And Mediator Together

The crux of the difference between an event aggregator and a mediator, and why these pattern names should not be interchanged with each other, is illustrated best by showing how they can be used together. The menu example for an event aggregator is the perfect place to introduce a mediator as well.

Clicking a menu item may trigger a series of changes throughout an application. Some of these changes will be independent of others, and using an event aggregator for this makes sense. Some of these changes may be internally related to each other, though, and may use a mediator to enact those changes. A mediator, then, could be set up to listen to the event aggregator. It could run it’s logic and process to facilitate and coordinate many objects that are related to each other, but unrelated to the original event source.

var MenuItem = Backbone.View.extend({

  events: {
    "click .thatThing": "clickedIt"
  },

  clickedIt: function(e){
    e.preventDefault();

    // assume this triggers "menu:click:foo"
    Backbone.trigger("menu:click:" + this.model.get("name"));
  }

});

// ... somewhere else in the app

var MyWorkflow = function(){
  Backbone.on("menu:click:foo", this.doStuff, this);
};

MyWorkflow.prototype.doStuff = function(){
  // instantiate multiple objects here.
  // set up event handlers for those objects.
  // coordinate all of the objects in to a meaninful workflow.
};

In this example, when the MenuItem with the right model is clicked, the “menu:click:foo” event will be triggered. An instance of the “MyWorkflow” object, assuming one is already instantiated, will handle this specific event and will coordinate all of the objects that it knows about, to create the desired user experience and workflow.

An event aggregator and a mediator have been combined to create a much more meaningful experience in both the code and the application itself. We now have a clean separation between the menu and the workflow through an event aggregator. And we are still keeping the workflow itself clean and maintainable through the use of a mediator.

Pattern Language: Semantics

There is one overriding point to make in all of this discussion: semantics. Communicating intent and semantics through the use of named patterns is only viable and only valid when all parties in a communication medium understand the language in the same way.

If I say “apple”, what am I talking about? Am I talking about a fruit? Or am I talking about a technology and consumer products company? As Sharon Cichelli says: “semantics will continue to be important, until we learn how to communicate in something other than language”.


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 AntiPatterns, Backbone, Design Patterns, Javascript, JQuery, Principles and Patterns, Workflow. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://twitter.com/cammerman Chris Ammerman

    I would love to see a more involved example of what goes into your mediators, as well as how you decide to use this pattern instead of something else. I’m much more experienced/comfortable with a layered model system for managing workflows or shared state (e.g. app/state models with shared state and workflow behavior, with viewmodels watching their events, and then views watching the viewmodels). Moving toward a more message/event-based design seems to work great until I have some central state that is either acted on by multiple small views, or is driving reactions in those views. Would you use a design like I described there, or would you have your workflow objects just hold onto the state that is relevant to them? Or is there just not much of a difference in that case?

    • http://mutedsolutions.com Derick Bailey

      the best example I have is in my BBCloneMail sample app for Marionette. I unfortunately named an object Marionette.Controller when in reality it should have been named Marionette.Mediator… but I use this object as my base for mediators and other object types a lot.

      A small example of a mediator is the “Inbox” controller: https://github.com/marionettejs/bbclonemail/blob/master/public/javascripts/bbclonemail/mail/mailbox/mailListView.js#L41-L60

      and a larger example would be the MailApp.Controller – the primary “mediator” / controller / workflow for the Mail App: https://github.com/marionettejs/bbclonemail/blob/master/public/javascripts/bbclonemail/mail/mailapp.js

      Mediators facilitate a layerd approach, IMO. Message based systems are not in conflict with layered systems, either. But you can’t completely decouple all of the workflow with messages, without paying a huge tax in the overhead of understanding the workflow and logic. When you have a set of related things that need to be coordinated in a very specific way, then you really do want something like a mediator to facilitate that coordination. it makes maintenance easier because you can understand the coordination better.

      My mediators definitely do hold on to state and act on that state. Often that state is represented by individual objects that it is coordinating, as well.

  • dstibrany

    Thanks for this article, it cleared up some confusion I have about mediators.

    Question: Which part of your code is responsible for setting up the mediators. Are the constructors definitions related to specific modules?

    I’m trying to understand where mediators would fit in the big picture of an MV* app.

    • http://mutedsolutions.com Derick Bailey

      Yeah, I tend to put them in charge of modules or components that need logic and coordination between objects.

  • http://twitter.com/jbasilio Jose Basilio

    There’s a typo towards the end of the section labeled “Similarities And Differences”, it says “dramtically different” (missing an A).

    • http://mutedsolutions.com Derick Bailey

      lol – i suck at typing, spelling, grammar :D

  • http://ifandelse.com Jim Cowart

    It’s impossible for me to resist wading into the discussion over semantics! :-) I have to confess – I feel like the pattern names are inadequately descriptive. “Event Aggregator” feels like Fowler wasn’t happy with “Message Broker” (I know, I know, that’s a huge can of worms as well). I’ve seen Mediators of all shapes and sizes, and am definitely guilty of describing Event Aggregators as ‘generalized mediators’ when they incorporate routing, etc. – but only because I’ve found the reaction to that description is often more receptive than dropping “message broker” on front-end-focused devs. On the server side, when a domain-specific mediator is fully flushed out, it usually effectively becomes an FSM managing a structured workflow (and ironically, many devs end up calling it a controller, for better or for worse). But really, my biggest beef is with “Pub/Sub” – it’s brought no end of confusion when devs utilize that term in the same project to describe both observer-pattern and generalized mediator/event aggregator/insert new buzzword phase. The other thing I’d say is that in many recent projects, I’ve seen this approach happen: message broker is wrapped with a domain-specific layer – arguably a facade or mediator – and workflow engines (FSMs, but arguably ‘mediators’ that, IMO, perform the function of “aggregating events and tracking state” much more specifically than the thing Fowler calls the “Event Aggregator”) participate as publishers & subscribers on the bus. (Hence my own bias against using “event aggregator” to describe what is more akin to a message broker.) What a tangled state of affairs, for lack of, ahem, better descriptions. What I *do* love, though, is that the constructs & concepts stand well (at least mentally) apart from the names, so once we arrive at a common conceptual definition – I’m with you 100%. :-)

    “semantics will continue to be important, until we learn how to communicate in something other than language” — great quote!

  • Steve Gentile

    I just don’t seem to really get it Derek. I see this ‘on’ complete code but I lack the context to know when it’s used and how that makes it a mediator.

    I certainly understand the jQuery on event – it’s an event listener to a click.

    So I’m still lost in respect to the mediator and events in this example?

  • Steve Gentile
    • http://mutedsolutions.com Derick Bailey

      that, too. i should have put that link in here somewhere :)

  • http://twitter.com/jameslockwood James Lockwood

    Interesting article, thanks Derick. I’m a big fan of using a layered mediator approach as it provides excellent workflow visibility, which is particularly useful when picking up a project you’re not familiar with, or when you’re maintaining old apps. When purely relying on event aggregators in complex apps it’s quite easy to lose track of how separate components are communicating. Mediators show your the logic in plain black-and-white for all to see, where as event aggregators require you to sniff around more!

    I’ve been playing around with creating a module libray that hugely relies on taking this layered approach (http://jameslockwood.github.io/module/#module-events ) when constructing your apps. It’s quite a neat solution to mediate across your usual Backbone object types :-)

  • Dmitriy Filipenko

    Can you please say, it’s good decision to use $( window ) or $( document ) use as a mediator?

    • Rob Pocklington

      For me, this is a natural choice. Some things like the $(window).scroll event, for example, live here natively. There shouldn’t be many events you need at a global level though, perhaps things like login, logout, feature toggles so I try to keep the coupling small and manageable.

      • http://mutedsolutions.com Derick Bailey

        having a global event aggregator is often a good idea for reasons that Rob points out. Using $(window) or $(document) is never a good idea, IMO. You don’t want to involve the DOM in your JavaScript, unless absolutely necessary. Call to / through DOM objects are expensive. It would be better to use a pure JavaScript object. The “Backbone” namespace object provides this, and it’s super easy to build your own. There are countless other implementations, as well. Just avoid the DOM’s objects when building your JavaScript event aggregator, if possible.

        Of course there are exceptions to this, when the DOM must be used: cross-frame communication, for example. But it’s a nice general rule, to avoid the DOM unless you know with certainty that it’s the only / best way.

  • Will Laramee

    The thing about the EventAggregator is that you
    must know the name of the event you want to subscribe to. If two unrelated
    modules/sub-applications need to communicate they have to share a common
    EventAgraegator, to me this event name referencing is coupling. Are you
    suggesting that it makes more since for each module to know nothing about the
    other at all, using a separate object (the mediator) to wire up their
    communications to one another?

    • http://mutedsolutions.com Derick Bailey

      I agree that the event naming is coupling. Like anything else we do, we need to understand the *correct* coupling between objects/components/etc that we are concerned with. It’s not that coupling is bad. Without it, our system won’t do anything. At all. But coupling things too tightly, or with relationships that shouldn’t exist, is as bad (not worse!) than decoupling things too far.

      sometimes it makes sense for two objects to be coupled through events. sometimes it makes sense for two object to not know about each other at all, and to have a 3rd party (mediator, or other pattern) involved.

    • Rob Pocklington

      Consider that most service oriented architecture is moving towards versioned APIs as ‘coupling’ between their services. These API URIs are coupling. You need something to couple two things… you can’t have nothing coupling two components. The goal is ‘loose’ coupling.

      IMO Event names/types are a good way to do this, and a natural way in Javascript. I also like the concept of ’round trip’ messages – check out Backbone.Courier https://github.com/rotundasoftware/backbone.courier

  • davidbiehl

    Thanks for the post. It helped clear some of my confusion about the responsibilities of a mediator. My question is: How does this pattern fit into a modular design?

    I am currently building a complex workflow with Marionette, and it doesn’t feel right. Currently, each step of the workflow lives in its own module with a Mediator to handle the workflow events of that step. These module level mediators trigger application events to progress through the workflow. It feels very disconnected, and I certainly cannot see the high level workflow at a glance, especially considering that the modules are in different files.

    Using a single high-level mediator to coordinate the workflow sounds like a good idea, but then wouldn’t that defeat the purpose of the modules? I fear it would be responsible for too many things, and get overly complex.

    I’m sure there’s a balance, and some lines need to be drawn between having a overly complex mediator and the workflow spread too thin between disparate modules. Can you offer any advice on identifying those boundaries?