Event Aggregator

Channel events from multiple objects into a single object to simplify registration for clients.

29 September 2004

This is part of the Further Enterprise Application Architecture development writing that I was doing in the mid 2000’s. Sadly too many other things have claimed my attention since, so I haven’t had time to work on them further, nor do I see much time in the foreseeable future. As such this material is very much in draft form and I won’t be doing any corrections or updates until I’m able to find time to work on it again.

A system with lots of objects can lead to complexities when a client wants to subscribe to events. The client has to find and register for each object individually, if each object has multiple events then each event requires a separate subscription.

An Event Aggregator acts as a single source of events for many objects. It registers for all the events of the many objects allowing clients to register with just the aggregator.

How it Works

An Event Aggregator is a simple element of indirection. In its simplest form you have it register with all the source objects you are interested in, and have all target objects register with the Event Aggregator. The Event Aggregator responds to any event from a source object by propagating that event to the target objects.

The simplest Event Aggregator aggregates events from multiple objects into itself, passing that same event onto its observers. An Event Aggregator can also generalize the event, converting events that are specific to a source object into a more generic event. That way the observers of the aggregators don't need to register for as many individual event types. This simplifies the registration process for observers, at the cost of being notified of events that may not have any material effect on the observer.

Since an Event Aggregator is based around observer, it's important to take into account all of the danger areas with observer.

When to Use It

Event Aggregator is a good choice when you have lots of objects that are potential event sources. Rather than have the observer deal with registering with them all, you can centralize the registration logic to the Event Aggregator. As well as simplifying registration, a Event Aggregator also simplifies the memory management issues in using observers.

Further Reading

You can think of an Event Aggregator as a particular form of Facade that focuses only on observer relationships.

Example: Watching Our Consultants (C#)

When someone joins Thoughtworks, they get immediately issued with a mobile phone. The old joke was that this was because Roy (the CEO) wanted to be able to call you any time day or night. So here I'm imagining a simple applications which tells Roy the location of all his consultants and whether they are available to be called on the phone.

Figure 1: Consultant class

To capture this we have consultant objects as in Figure 1. When consultants move around we capture this by messages about these movements indicating which consultant moved and where they are now.

When these messages are received by the system they are passed to a consultant object to process them and update the consultant's status. Updating the current location is rather obvious, updating the availability has some mild computation - essentially each consultant has a home and can choose whether they are available while at home. (It's assumed that when on the road they are always subject to Roy's telephonic whims.)

class Consultant...

  public bool IsAvailable {
    get {return IsAvailableAt(_current_location);}
  }
  private bool IsAvailableAt(string location) {
    return (location == Home) ? _availableAtHome : true;
  }

So when the consultant handles the movement message it has to worry about two things that may change - the current location and the availability. It signals a separate event for each.

class Consultant...

  public void HandleMovement(MsgMovement movement) {
    if (_current_location != movement.Place) {
      if (LocationChanged != null) LocationChanged(this, EventArgs.Empty);
      if (IsAvailableAt(movement.Place) != IsAvailable) {
        if (AvailabilityChanged != null) AvailabilityChanged(this, EventArgs.Empty);
      }
      _current_location = movement.Place;
    }
  }

The screen for this application might choose to register for these events for all the consultants, but here I'll use an Event Aggregator. The Event Aggregator monitors an consultant by registering for all its events.

class EventAggregator...

  public void Listen (Consultant subject) {
    subject.LocationChanged += new Consultant.ConsultantEventHandler(HandleConsultantLocationChanged);
    subject.AvailabilityChanged += new Consultant.ConsultantEventHandler(HandleConsultantAvailabilityChanged);
  }

When it receives an event it signals two events, a specific one for the type of change and a general one just indicating a change has occurred. This allows clients to register to the granularity they wish.

class EventAggregator...

  private void HandleConsultantLocationChanged (Consultant consultant, EventArgs args) {
    if (ConsultantLocationChanged != null) ConsultantLocationChanged(consultant, args); 
    if (ConsultantChanged != null) ConsultantChanged(consultant, args);
  }
  private void HandleConsultantAvailabilityChanged (Consultant consultant, EventArgs args) {
    if (ConsultantAvailabilityChanged != null) ConsultantAvailabilityChanged(consultant, args);
    if (ConsultantChanged != null) ConsultantChanged(consultant, args);
  }
  public event Consultant.ConsultantEventHandler ConsultantChanged;
  public event Consultant.ConsultantEventHandler ConsultantLocationChanged;
  public event Consultant.ConsultantEventHandler ConsultantAvailabilityChanged;

Most of the time, I'd suggest using just a single coarse-grained event. However this gives a good example of how you can react to the incoming events by providing a variety of different reactions depending on the needs of your clients. This also allows the Event Aggregator to act as an Adapter as well as a facade.