-
Notifications
You must be signed in to change notification settings - Fork 191
Message Bus vs "Pub Sub"
JavaScript developers often throw around the term "pub/sub" when referring to a number of "observer/observable" patterns. Quite technically, anything that "publishes" data that other components are interested in is a "publisher" and the listeners are "subscribers". However, it's critical that developers understand that not all "pub/sub" approaches are interchangeable, and some work better in certain situations than others. A message bus is a very specific form of "pub/sub", and its implementation details will have a very different kind of impact on an application than if standard event delegation were used in its place.
Consider the following approaches:
// over-simplified event delegation/broadcasting
var publisher = {
subscribers: [],
publish: function() {
var args = [].slice.call(arguments, 0);
_.each(this.subscribers, function( subscriber ) {
subscriber.apply(this, args);
});
}
}
publisher.publish("George", "Washington", { country: "United States", year: 1791 } );
// over-simplified message bus
var msgBus = {
subscribers: [],
publish: function( data ) {
_.each(this.subscribers, function( subscriber ) {
subscriber.call(this, data);
});
}
}
msgBus.publish({
firstName: "George",
lastName: "Washington",
otherData: {
country: "United States",
year: 1791
}
});
The first approach more closely follows the paradigm of event delegation. Subscribers add themselves to the queue and expect a specific argument signature to be passed into their callbacks when the event is invoked.
The second approach follows a "message bus" paradigm, where the subscribers expect a message body (or sometimes a full envelope). The envelope is consistent for every publish - meaning that the subscriber callbacks all follow a common & consistent convention - yet the body of the data can vary as much as a publisher wants it to.
Both approaches have strengths, though postal has staked itself on a design opinion that the second approach offers more flexibility with fewer drawbacks (for example, your subscribers aren't constantly changing method signatures, and it's possible to decorate a message envelope with metadata describing more about the message than just the data in the message body). For internal events (within a module), the Observer pattern is a great fit. When it comes to communications between modules, a message bus, with a standard envelope and channels to segment topics, offers much more power and flexibility.
By "modules", I mean discrete/cohesive components. This might be an AMD or CommonJS module, or a "view" or a "view model" in an MVC framework, etc.
postal.js strongly adheres to the idea that a consistent 'envelope pattern' is essential. Every postal message is structured with along these lines:
{
channel: "ChannelName",
topic: "Topic.String",
timeStamp: "2012-05-22T16:59:55.009Z",
data: {
firstName: "James",
lastName: "Madison"
}
}
Publishers can decorate the envelope with additional metadata - for example, a "replyTo" topic can be included, or a correlationId (which would tie several messages on a given channel/topic together as related).
When you call publish
from a ChannelDefinition
instance in postal, the channel, topic and timeStamp values are added for you automatically (though it is possible to override the topic in a ChannelDefinition
publish). You cannot override the channel or timeStamp values when publishing from a ChannelDefinition
.
When you call postal.publish
, you must provide the channel and topic in addition to the data being published, but you cannot override the timeStamp value (it's added to the envelope internally by postal).