Event Bus Pattern via Native EventEmmiter Class
Ivan V.
Posted on November 2, 2019
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.
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.
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 November 2, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.