loading...

Event Bus Pattern via Native EventEmmiter Class

ivandotv profile image Ivan V. ・3 min read

Event Bus is one of the most popular patterns for decoupling parts of your system (components) or anything else that shouldn't be tightly coupled together.
If you have a large scale application with a lot of components interacting with each other, you may want to make your components communicate via intermediary and maintain loose coupling and separation of concerns principles.

In this article, I'm going going to introduce you to my event bus implementation via native Node.js EventEmitter class.

How it works

The point of the event bus pattern is to have components be able to issue updates to their state or request some action to be taken via an intermediary (event bus) so they are completely decoupled from the rest of the system (other components), and because of that they can easily be removed from the system and other components can be easily added.

event bus simple diagram

Implementation

My implementation of the event bus pattern is not dispatching just simple messages, it has two concepts: channel and topic.

Event bus can contain any number of channels, and every channel can contain any number of topics.

You can listen to the channel and get notified on any topic on that channel or you can listen to a particular topic and be notified only when there are updates on that particular topic.

channel communication

In the above diagram TODO Component will ask the event bus to create the channel: TODO with topics added, removed and updated.
When the channel is created it is available to be broadcasted on.

Depending on what is going on inside the TODO Component at some point the component will ask the event bus to broadcast on the TODO channel with various topics.

Logging Component wants to notified about everything regarding the TODO's (added, updated, removed) so in this case, the component will subscribe itself to the TODO channel directly (no need to subscribe to each topic individually).

Component X only wants to know when todos are removed so it will listen to (subscribe to) only to the removed topic on the TODO channel.

Component Y will subscribe only to the updated topic on the TODO channel.

Event

Every listener receives the event with these properties

const event = {
    channel:'TODO', // channel name
    topic:'removed' // topic name
    payload:{ // custom object with your custom payload (data)
        anyting:'you want'
    }
}

Let's create the previous diagram in code:

const { EventBus } = require("estacion")
// or import { EventBus } from 'estacion'

// create the event bus
const bus = new EventBus()

// create the channel
const todoChannel = bus.channel("TODO")

// create topics on the channel (optional)
const todoCreatedTopic = todoChannel.topic("created")
const todoUpdatedTopic = todoChannel.topic("updated")
const todoRemovedTopic = todoChannel.topic("removed")

// create listeners
// logingComponent ( in this case just a simple function)
const loggingComponent = event => {
  console.log(event.channel) //channel name: TODO
  console.log(event.topic) // topic name: removed OR added or UPDATED
  console.log(event.payload) // custom payload (data) from the event
}

const componentX = event => {
  console.log(event.channel) // TODO
  console.log(event.topic) // removed only !
  console.log(event.payload) // custom payload from the event
}

const componentY = event => {
  console.log(event.channel) // TODO
  console.log(event.topic) // updated only !
  console.log(event.payload) // custom payload from the event
}

// Connect listeners to the appropriate channel or topic

// add loggingComponent directly to the channel
todoChannel.addListener(loggingComponent)

// add componentX only to the "removed" topic
todoRemovedTopic.addListener(componentX)
// or get to the topic from the channel
todoChannel.topic("removed").addListener(componentX)

// add componentY only to the "updated" topic
todoUpdatedTopic.addListener(componentY)
// or get to the topic from the channel
todoChannel.topic("updated").addListener(componentY)

// emit when todo is removed
todoRemovedTopic.emit({ id: "3413241" })
// or
todoChannel.topic("removed").emit({ id: "3413241" })

// emit when todo is created
todoCreatedTopic.emit({
  id: "3413241",
  title: "Buy Milk",
  content: "Lorem Ipsum"
})

Conclusion

  • You can have any number of event bus instances, channels and topics.
  • Easily create and remove channels and topics (all listeners are automatically unsubscribed when channel or topic is destroyed)

It can be used in the browser via events module which is automatically included by the bundlers like webpack and browserify.

This is just a simple, minimal demo to get you started with the event bus. This event bus module is written in TypeScript and it has everything you need to manage your events.

Head over to the repository for more examples and in-depth explanations.

Or download from npm

Posted on Nov 2 '19 by:

Discussion

markdown guide