GitHub LinkedIn RSS
Sunday, June 22, 2014

Publish-Subscribe Pattern with Postal.js


Last week we talked about testing JavaScript Testing with Jasmine and today I'd like to continue our conversation about testing rather from different angle - separation of concerns. Once the code slips developers' fingers, it is hard to find time and strength to rewrite, it making it more testable. What works, may never be rewritten, sounds a bit like Iron island refrain What is dead, may never die :) Hmm - sorry for that! Nearly all today's applications have some calls to somewhere to retrieve the data. Front side retrieves from the server. Server side retrieves from services or database. Some manipulate it, others just format it and present as it is. Take a look at the following examples:
function someLogic() {
 $.get("ajax/test.html", function (data) {
  $(".result").html(data);
 }); 
}
function someLogic() {
 dataManager.getClients(function (err, data) {
  if (!err) {
   doSomething(data);
  }  
 }); 
}
How can you test it? First of all the server is needed to send the data. Secondly the data should always be the same to write our test specs correctly. Surely both are achievable, however wouldn't it be great if you could write the code in a way it's both clear and testable. This is where publish–subscribe pattern shines or pub/sub. Following the pattern ensures that your data receivers, or  subscribers, are totally separated from data producers, or publishers. The concept is represented in the following illustration:


A publisher observes the bus and when an event of interest is observed, a notification is created and sent to the notification engine. The notification is then matched against the subscribers that have expressed interest in the notification and prepared for the delivery. This model separates the management of the subscriptions, the matching process and the final delivery to the subscribers. There are currently two JavaScript libraries, which implement this pattern: Postal.js and Amplify.js. Since Amplify.js doesn't support server side programming and in the front side domain, Postal.js offers much more extensibility, we'll be focusing our conversation of Postal.js only.

Postal.js


Postal.js is an in-memory message bus - very loosely inspired by AMQP - written in JavaScript. Postal.js runs in the browser, or on the server using node.js. It takes the familiar "eventing-style" paradigm (of which most JavaScript developers are familiar) and extends it by providing "broker" and subscriber implementations, which are more sophisticated than what you typically find in simple event delegation.

In order to start working with the library, you should also add a reference to ConduitJS, since it's dependent on it. At first we need to create a channel, through which your messages will flow. The beauty of Postal.js is your ability to create named channels and separate completely your service buses by domains. If you don't need this ability, just create a default one without passing any name. Having our channel in place, we can subscribe to the events of interest and publish data to it. We called our event name.change, however you can call yours as you like. The dot separation though will come useful in the future.
var channel = postal.channel();
// subscribe to 'name.change' topics
var subscriber = channel.subscribe("name.change", function (data) {
 $("#example1").html("Name: " + data.name);
});
// And someone publishes a name change:
channel.publish("name.change", { name : "Dr. Who" });
// To unsubscribe, you:
subscriber.unsubscribe();
The data will flow through Postal's engine and be delivered to all the subscribers. The example present was rather dull, don't you agree? Let's spice it up by subscribing to change event of any object. We'll use the dot convention presented earlier and subscribe to event following the pattern *.change.
var subscriber = channel.subscribe("*.changed", function (data) {
 var i = $("
  • " + data.type + " -> " + data.value + "
  • "); i.appendTo("#example2"); } ); channel.publish("name.changed", {type: "Name", value: "John"}); channel.publish("country.changed",{type: "Country", value: "USA"}); subscriber.unsubscribe();
    As you can see both name.changed and country.changed are caught now. The pattern can be changed to name.*, which will catch all events related to name object like name.init or name.change. Postal.js supports additional patterns, have a look at their documentation to learn more.

    As said before, there are a lot of plugins written for Postal.js, making this library insanely useful. The most prominent are postal.when, providing even more targeted functionality to subscribers, and postal.federation delivering a framework to federate (or bridge) multiple instances of postal together, across various boundaries (frame/window, websocket, redis pub/sub, 0mq, etc.).

    We'll be talking more about messaging patterns in the future and various design patterns as well.