Inspired by BBC responsive news i have spent a lot of time over the last few months investigating and then buiding around EventEmitter. EventEmitter is a lightweight PubSub library with a node.js ancestry.
I have never developed using custom events before, so it’s been a tight learning curve. I guess many people are in this position so hopefully sharing these notes with give you a head start.
I have found event naming to be really difficult. In an ideal world an event name is self describing, for example, “carousel:load:tab” seems pretty clear. However, on deeper inspection its not quite as clear as it could be. Its not clear if its a request (eg, please show a tab) or a command (here is a tab and some data, display it).
I went with a “carousel:load” prefix for requests which gave us “carousel:load:tab” and “carousle:load:next” etc. On command side i went with a “carousel:display” prefix.
This seems to work well, though it’s a little nugget of infomation you need to know in order to understand our architecture. For the next iteration i suspect i may take a more declarative style. Something more like “carousel:display:request” i guess. Not sure.
Which data to send with events.
The events (or messages in PubSub parlence) have consisted of a name and a payload of data. Following on form the last example, “carousel:load:tab” carries a payload object with two properties. The tab name and the tab url.
At first i tried to keep events to a minimuim, with each module only have one or two events associated. This led to many event having massive payloads which contained everything i knew about the object to support all of the use cases. This led to some complex event handlers which did way too much.
I have now adopted a model with a few more events but with less data per event. So for example, “carousel:display:start” got split into “carousel:display:uncached” and “carousel:display:cached”. The event names are more explcit which on balence i feel is a good thing…. Its not perfect though.
Naming events this way does reveals some of the inner workings of the carousel. In this case, it expliciting indicates a difference between cached displays and uncachced displays. The alternative was to have a property on a more generic event. I figuired explcit naming would be easier to understand for developers in the future.
Structuring Features via modules.
Roughly speaking most features split into two modules. A Core module (with the business logic) and a UI module which deals with the DOM. The modules then commuincate using events.
Heres an example of the event stream which results form clicking the “next tab” button on a carousel.
1. The UI Module converts DOM click events to a pubsub events called “carousel:load:next”. It does very little logic at this level.
2. The Core module listens for the “carousel:load:next” event. The core then figures out the correct response, in this case loading some data and firing a “carousel:display” event with the data attached.
3. The UI module listens for “carousel:display” event and then updates the page accordingly.
Having the Core in the middle decouples the business logic form the UI. You can swap the UI out to whatever makes sense at the time. You can also reuse the core across platforms. I also store state in the core. The core knows the current tab and everything else defers to it for actions requiring that information.
So thats about it for now, i am still working this stuff out but i am also having alot of fun. If you have never tried pubsub based JS development i would encorage you to give it a go.