Publisher/Subscriber Architecture
Currently in Maestrale, we often use webcomponent to model the visualization of heterogeneous data from different sources: we have webcomponents that deal with the visualization of the data coming from microservices that query PLCs, or microservices that return management statistics of industrial appliances.
The interface of these components, detached from each other, must be ‘responsive’, change rapidly and simultaneously based on certain user inputs, or on certain states of the application.
To manage the communication between the different components, essentially independent, and to manage the flow of actions, we decided to use a publisher / subscriber architecture: senders and recipients communicate through a dispatcher (or bus or communication channel) by sending messages.
In this case, publishers (message senders) and subscribers (message recipients) come into play. This is the scheme of action
- One or more publishers (webcomponent for us) publish a message (or payload) on the dispatcher (an EventBus)
- One or more subscribers (specific controllers of our MVC framework or webcomponent) subscribe to the reception of one or more types of message
- The dispatcher forwards each message to the subscribers interested in the specific message
Event Bus
In order to create a dispatcher of messages from/to webcomponent, that were managed on the framework side, we have implemented a basic EventBus: for our case, a globally accessible, callable event management platform for publishing or subscribing messages, from any JavaScript object.
The implementation is based on the use of an object that supports events. We used a simple Element of the DOM that runs the dispatch of events and to which event lists are recorded… It is easier to see it :)
const EventBus = () => {
let _bus = document.createElement('div');
const _register = (event, callback) => {
_bus.addEventListener(event, callback);
};
const _remove = (event, callback) => {
_bus.removeEventListener(event, callback);
};
const _fire = (event, detail = {}) => {
_bus.dispatchEvent(new CustomEvent(event, { detail }));
};
return {
register: _register,
remove: _remove,
fire: _fire
}
};
window.EventBus = EventBus();
Use
Suppose we have a dummy webcomponent consisting of a button-bar: the push buttons of the button-bar trigger methods that change the state of the bar and of the buttons. We also want the click on a specific button triggers the update of another webcomponents which exposes data in a grid after a REST call to a service.
The webcomponent of the push-button panel must use the EventBus to trigger the custom event ‘BUTTONBAR_CLICK_BTN’ at the click of the buttons on the button-bar, if the button is the one chosen (of course this depends on the architecture and domain analysis of the application to be developed)
// webcomponents-buttonbar.js
const $btn = document.querySelector(".buttonbar button");
$btn.addEventListener("click", (e) => {
// make stuff on UI of webcomponents
// fire event
const triggerFire = <some condition to perform triggering>;
triggerFire && window.EventBus.fire("BUTTONBAR_CLICK_BTN", { target: e.target });
});
The webcomponent of the grid in the meantime will take care of registering a listener for the specific event ‘BUTTONBAR_CLICK_BTN’. When the event is triggered by the publisher (webcomponent-buttonbar), the subscriber listner (webcomponent-grid) will be invoked.
// webcomponents-grid.js
connectedCallback() {
window.EventBus.register("BUTTONBAR_CLICK_BTN", this.updateGrid({store: this.STORE}));
}
updateGrid() {
//perform some REST call for retrieving data
//UI update (better in other method)
}
This is just a simple example of the possible use of an EventBus: to dialogue different ‘objects’ that do not reside in any dependency hierarchy
Advantages
More than advantages to using an EventBus, we can talk about the advantages of using the Publisher/Subscriber architecture. Among others, I have identified those that, in my opinion, can turn out really advantageous in the web.
Separation of responsibilities: each component (not intended only as a web component) of the web application can deal with its own specificities, the exchange of messages and routing are delegated to the dispatcher.
A web application can communicate with one or more independently developed services or components, which could use different platforms, programming languagesand communication protocols: they only have to register with the dispatcher to ‘listen’ to one or more messages, and they only have to publish on the dispatcher to communicate with other components
You can schedule or postpone the use of messages: subscribers can pick up payloads and run the specific listner according to certain hourly thresholds, or the publisher can route payloads on a specific schedule
Increase and improve testability: buses can be monitored and payloads sent checked or recorded as part of a serious integration testing strategy
This is only a brief overview of the use of this pattern. I have highlighted only advantages, but, of course, there are also disadvantages to be analized in specific use cases.