Skip to content

Instantly share code, notes, and snippets.

@mpj
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpj/f0088b1b868bb34e6065 to your computer and use it in GitHub Desktop.
Save mpj/f0088b1b868bb34e6065 to your computer and use it in GitHub Desktop.
Snurra sketch

Snurra - the streaming message bus.

Snurra is a fresh take on message bus pattern by using streams as the interface instead of the more traditional callback pattern.

Snurra syntax

Let's walk through the basic syntax of Snurra.

var _ = require('highland');
var snurra = require('snurra');
var bus = snurra();

bus( 'user-change' ).write( { name : 'Dr. Wafflehat' });

_(bus( 'user-change' )).each(function(change) {
  $( '#example1' ).html( 'Name: ' + change.name );
})

We first create Snurra bus:

var bus = snurra();

A message bus is like a switchboard for your code, that different parts of your program will use to communicate without knowing about each other directly. This decouples the parts of your program from one another, making it easier to reason about your program and to replace individual parts of your application as development progresses.

bus( 'user-change' ).write( { name : 'Dr. Wafflehat' });

The line above sends a message on the topic with the name user-change. Topics are always strings, and messages can be numbers, strings and JSON-serializable objects. (Sidenote: This means that you cannot send functions through Snurrain any way. Snurra is a bit opinionated, and considers passing functions around your app to be smelly code.)

bus('user-change')

Let's dissect things further. The above line will return a topic stream for the topic user-change. A topic stream is a duplex stream that is both a subscriber and a publisher of a topic at the same time. I.e. when you write a message to the topic stream, you'll be publishing that message on that topic, and when you read from it, you'll receive the messages published on that topic. The latter is what happens on the final part of the code example:

_(bus( 'user-change' )).each(function(change) {
  $( '#example1' ).html( 'Name: ' + change.name );
})

What is highland?

The underscore above is not Underscore.js. It's highland.js, the new library by caolan, the developer behind async.js. Highland does for streams what Underscore did for arrays and jQuery did for DOM Elements - it adds a delicious API on top that allows you to do much more and easier.

Snurra deals only with vanilla node streams and doesn't require you to use highland.js whatsoever - they just play fantastically nice together and make these examples much more readable and demonstrative of the power of this way of programming.

Advanced filtering

In case that you want to do some more complex subscribing than than just listening to a topic, calling bus.envelopes() will give you a steam of all envelopes , which is simply an object with a topic and a message property:

_(bus.envelopes()).filter(function(envelope) {
  // envelope is an object with a topic and message property.
  // read envelope.topic and filter out only the messages you want
})

Do note that filtering based on envelopes will be coupled to Snurra, so take care when using it so that most of the streams in your main program stays unaware of Snurra.

Beyond this, Snurra doesn't offer any additional, built-in filtering functionality. The rationale for this is that the topic stream interface allows the user of Snurra to use the high-level stream libraries like highland.js which are so capable that weighing down Snurra with filtering capabilities would not make sense:

var unhandledOrders = _(bus('orders'))
  .filter(function(order) { return !order.shippingdate });

unhandledOrdersStream.each(function(order) {
  // do stuff with order that is not handled
})

Terminal dump

One of the nice parts about having a message bus is that you can inspect what messages are being sent on what topics, giving you a much better sense of what is happening in your app, and in what order. This makes it a lot easier to see where things go wrong. For example, by outputting everything that happens as to your terminal:

bus.envelopes().pipe(process.stdout);

Example log output:

{
  topic: 'my-topic',
  message: {
    myMessageProperty: 123
  }
}

Tattletale streams

Since a topic stream is a duplex stream that will read whatever is sent, it will in practice act as passthrough stream. This can be used as a much better alternative to console.log, putting it as a middle-man stream where you want to check what is being passed between two streams that are not normally using Snurra:

_(bus('some-topic'))
  .map(function(message) {
    // we suspect there might be some bug in this logic
  })
  .through(bus('debug-mapper-output'))
  .filter(function(message) {
    // do some more stuff
  })
  .pipe(bus('some-other-topic'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment