The Coordinator pattern

I wanted to share a pattern that we’ve been using lately in our
Backbone/Marionette application. It’s a pattern that we strongly
recommend for separating monolithic applications into components.

UI-heavy applications and components

With the rise of reactive programming and frameworks like Facebook’s
React its becoming more apparent that state should never be kept on
the View layer. The programs we write should only deal with state
and when this changes it should update the views reactively.

We’ve experienced how much easier it is to maintain/test the programs
written with this approach. That being said, most architecture sins are
committed on writing UI-heavy applications. Those have a lot of working
parts that have to talk to each other. Its easy to fall into the trap
and have all these parts being coupled with each other. To avoid this
scenario, those apps would be better implemented with components.

A component is a reusable view that has a state, can be interacted with
and can be composed.

Separating a problem into components is not an easy task, and we soon began to ask ourselves the following questions:

  • Where do I keep the component state?
  • How do I make two components speak to each other?
  • How can a component request the state of another component?
  • How do I achieve all of the above without them knowing each other?

You can answer all this questions with the Coordinator pattern.

Coordinator

What is a Coordinator?

A Coordinator is an event aggregator that can hold state. I like to describe
them like “little sticks” that are used to tie components together. They hold
component’s shared state and serve as a localized bus of events.

Components can use that bus to communicate with each other.

Imagine we have a UI with 3 components:

  • Country selector
  • State selector
  • Map

The map reacts to country/state changes. The state selector updates itself
on selecting a country. We won’t store any state on the views, but we will use a shared Coordinator that will allow them to communicate without knowing each other.

Let’s represent this with a dummy Marionette application:

CountrySelector = Backbone.Marionette.ItemView.extend({
  events: { 'change': 'onChange' }
, onChange: function () {
    this.model.set('country', this.$el.val());
  }
, serializeData: function () {
    return {country: this.model.get('country')};
  }
});

StateSelector = Backbone.Marionette.ItemView.extend({
  events: { 'change': 'onChange' }
, onChange: function () {
    this.model.set('state', this.$el.val());
  }
, serializeData: function () {
    return {state: this.model.get('state')};
  }
});

Map = Backbone.Marionette.ItemView.extend({
  serializeData: function () {
    return {
      state: this.model.get('state')
    , country: this.model.get('country')
    };
  }
});

MainApplication = Backbone.View.extend({
  initialize: function () {
    var coordinator = new Backbone.Model()
      , options = {model: coordinator};

    this.map = new Map(options)
    this.state_sel = new StateSelector(options)
    this.country_sel = new CountrySelector(options);

    this.listenTo(coordinator, 'change:country', this.state_sel.render);
    this.listenTo(coordinator, 'change', this.map.render);
  }
});

Benefits of the Coordinator pattern

We’ve seen that this problem is not only bound to Views and UI-heavy
applications. Any kind of monolithic application benefits from being
split into smaller self-contained components.
These components need a way to interact. Coordinators are an awesome solution!

One of the best things about this pattern is that most of the
environments / frameworks have the necessary tools out of the box:
We are using them in Rails and soon in our mobile
apps too. Give them a try!