TeqFW: Event Basics
Publication date: 2022-01-05JavaScript is well suited to asynchronous programming, where events are central. This article introduces how Tequila Framework uses events to coordinate work within the browser or server and across the boundary between them.
What is an event?
An event is a meaningful state change, such as “the user has authenticated”. It carries data — for example, a user identifier — arises from an action, and can cause further actions such as loading the user profile.
In the simplest form there are two functional objects and one data object: an Event Producer changes state and emits an Event Message; one or more Event Handlers react. An event has a type, such as onClick, and a message with related data, such as a MouseEvent.
Handlers react asynchronously, can change application state, and can emit further events. This makes an application more flexible than a chain of synchronous calls, but it also requires discipline and observability.
Local and cross-boundary events
A Teq application usually has a frontend in a browser and a Node.js backend. If producer and handler run in the same process, the event is local. If they are on opposite sides, it is cross-boundary.
Cross-boundary messages travel over the Internet as JSON. Their data must therefore be serializable and deserializable without loss. A local message stays within one process and is not subject to that transport constraint.
Event names
Each event type should be unique across the application. Teq namespaces make an event’s ES module name a natural identifier:
Vnd_Plug_Front_Event_Net_Status_Changed: a local frontend event;Vnd_Plug_Back_Event_Sale_Order_Registered: a local backend event;Vnd_Plug_Shared_Event_Front_Sale_Order_Confirmed: a browser-originated cross-boundary event;Vnd_Plug_Shared_Event_Back_Sale_Order_Registered: a backend-originated cross-boundary event.
The ES module encapsulates not only the event name but also the DTO shape of its message. That makes the contract explicit and reusable on both sides.
Handling local events
A local producer supports publishing and subscription:
emit(eventName, message) {}
subscribe(eventName, handler) {}
unsubscribe(subscription) {}
A subscriber tells the producer which event it needs and supplies a handler. When the state changes, the producer sends the message to that handler.
Because both parties are on the frontend or both on the backend, this is comparatively simple.
Handling cross-boundary events
With a browser producer and server handler — or the reverse — a mobile application has an unreliable communication channel in between.
The producer sends messages to a queue that watches the channel and delivers when possible. On the other side, a representative or “embassy” lets local handlers subscribe to events from the remote side. Once it receives a network message, it routes it to the matching handler. The same principle works in both directions.
A production design must also define message identity, ordering, retry and deduplication semantics; a queue alone does not make delivery exactly-once.
Summary
Event-driven architecture matches JavaScript’s asynchronous nature and works well in environments that change unpredictably. Its cost is less linear control flow. Event-driven applications therefore need purposeful logging and tracing on both client and server so people can understand what happened and recover from failures.