"Event driven" is a programming paradigm that consists of user actions/interactions that trigger the next flow of events. Modern dynamic apps are built to respond to events, user clicks, voice commands, and other interactions in an asynchronous programming model.
Typically there is an event loop that listens for new events and triggers some event handler. PostgreSQL, similar to any SQL-based database, supports an event-based execution in the form of triggers.
A trigger allows registering a procedure to be executed at a specified timing when an event is produced. The timing can be one of before, after, or instead of, and the event can be one of insert, update, delete, or truncate.
These procedures can be either stored routines or external programs that run on distributed systems, performing a piece of logic or other function according to your application's needs.
Defining 3factor applications
3factor is an architecture pattern for full-stack applications. 3factor enables building apps that are robust and scalable from the get-go by using modern API architectures along with the power of the Cloud. At it's core, 3factor aligns with event driven programming with the addition of opting for realtime GraphQL APIs as the delivery model. The three factors in the 3factor application are:
In addition to the descriptive query language of GraphQL, the GraphQL specification provides for subscriptions out-of-the-box, which allow us to handle synchronous behaviors that themselves then act as asynchronous triggers which will execute our event handling program for us.
The other two programming paradigms of reliable eventing and async serverless will be handled in the next section.
An event system to invoke business logic. Instead of writing long procedures which manipulate state in-memory and persist them in the database at the end, perform simple operations (e.g. CRUD on a resource) that generates events and pushes them to an event queue.
It is recommended that the events be persisted in an event log to keep the entire history of state changes. The event system then delivers these events to business logic routines.
The event system should have the following properties:
Atomic: Mutations to the application state should atomically create event(s) - all succeed or all fail.
Reliable: Events should be delivered (to any consumer) with an "at least once" guarantee.
These contracts in the event driven, programming paradigm allows for a concept known as "eventual consistency".
Event Queue and Event Loop
The event loop processes the event queue and derives its name from the pattern it's typically referenced as in the various programming languages it's implemented in. Our application triggers events through user interactions, CRON schedules, or in response to other events.
These events form a queue to be executed and our event loop processes these in the order they arrived in a synchronous manner. This synchronous processing of the queue allows us to guarantee that our system can respond predictably to the amount of load it encounters.
Instead of processing all events at once, we are able to methodically delegate these tasks to the systems designed to handle them where they perform their routine.
Event driven programming lives and dies based on the resilience of this queue and loop paradigm. Choosing a system that is able to robustly handle the needs of your queue is a critical component to an effective event driven approach.
The event triggers system, or event programming, is horizontally scalable - if you need to handle more events, just throw more resources at it! Event handlers can be defined as serverless functions that take care of program execution or other serverless applications that act as event handlers.
Asynchronous Event Handler
When designing event driven applications, the asynchronous functions or external serverless applications that act as your event handlers need to accommodate a few idiosyncrasies of the eventing system.
Idempotent execution model
Event driven programming may send the same event multiple times, our event handlers need to be prepared for this reality and respond accordingly - this is a fundamental component of event driven.
Out-of-order event processing
Network instability issues, latency, or even sharks eating the undersea copper are part of the many reasons events may not receive in the order that users submitted their event. We need to be able to handle user-time inside of our event handler to provide for a consistent and fair application experience.
Apps driven by events improve user experience. The user interface is not blocked and is instead driven by asynchronous events which can be subscribed to for updates. This is a paradigm shift from the previous model that was ruled by the spinning progress wheel or loading bar. Now, we can return some minimal guarantee that the event was added to the queue, update the page accordingly with the return value and then listen for any further updates in the future in our code.
Improved error recovery
The biggest benefit of making applications event driven is the way in which error handling and recovery can be improved. If the event failed to get added to the queue, our UI can provide a smooth opportunity to retry on failure – making the logic simple when compared to traditional architectures with endless state callback functions and complicated loops.
Optimistic updates: better for end-users
One of the benefits of this event driven approach is that we can notify the user of what our expected UI state will be. If we trigger an action, that is guaranteed to be reliable and atomic, we know that it will be a matter of time until our application interface gets the response we are waiting for. This pattern can be seen with pulsing gray boxes or outlines that are often used as placeholders for the different use cases identified.