Open for commentsSelect text to comment

Note

In implicit invocation systems, functions are not in direct communication with each other. The event-based architectural style is one design for such a system. This style derives from the event-driven programming paradigm.

Event Based

In event-based architectural styles, the fundamental elements in the system are events. Events are both indicators of change in the system and triggers to functions. Events can be signals, user inputs, messages, or data from other functions or programs.

Under the event-driven programming paradigm, functions take the form of event generators and even consumers. Event generators send events, and event consumers receive and process these events. A function can be both an event generator and an event consumer.

In the event-based architectural style, functions are not explicitly called. Instead, event consumers are based on events sent from event generators. Event-based functions experience implicit invocation, where communication between functions is mediated by an event bus. An event bus is the connector between all event generators and consumers in the system.

To achieve this structure, an event and an event consumer must be bound via an event bus. This means that each event consumer registers with the event bus to be notified of certain events.

When the event bus detects an event, it distributes the event to all appropriate event consumers. The event bus is a critical part of the system. The observer design pattern actually manifests the event-based architectural style.

One means of implementing the event bus is to structure the system to have a main loop that continually listens for events. When an event is detected, the loop calls all the functions bound to that event. This function must likely also be an event generator, because often an event consumer will have to notify other functions when it has completed its task or to send a state change. This function will implicitly invoke other functions to run after it has completed. It does this by sending out an event to the event bus. When an event reaches the event bus, corresponding event consumers will be triggered and the next computation can take place.

When designing your system, it is important to take into account that indirect communication between functions may not be as efficient as explicit function invocation. In event-based architectural styles, event generators do not necessarily know which functions will be consuming their events, and likewise, event consumers do not necessarily know who is generating the events they handle. This is called loose coupling, which makes it easier for systems to evolve and scale up, adding a new function requires only registering a new event-function pair to the event bus and adding a new event consumer.

Updating objects and data in an event-based system must also be considered. Event consumers can change shared states as they process an event if objects in the system are globally accessible. However, this allows event consumers to be called asynchronously, that is, an event consumer does not need to wait for other event consumers to finish running before running itself. Two event consumers could be running at the same time on shared data.

This asynchronous design may increase efficiency of the system, but it can also result in race conditions. Race conditions mean that the correct behaviors of functions is sensitive to timing or order, so shared data may not be updated correctly.

Functions can be coordinated for access to shared data through a semaphore. A semaphore is a variable or abstract data type that toggles between two values, available and unavailable, and that is used to control access to shared data. Available indicates that the shared data is not in use, and unavailable indicates that the shared data is in use by a function.

In general, a semaphore has a special operation to check and toggle its value in one step. Any function that would like to access the shared data must first check the value of the semaphore. If the value is set to unavailable, then the function must wait. If the value is set to available, the function can access the shared data, and the value is set to unavailable. When the function no longer needs access, it will toggle the semaphore value to available.

If an object is not globally accessible and its state is changed by an event consumer, then some indication must be sent back to the event bus before the function ends. Event consumers can thus be aware of the state change to that event.

In event-based architectural styles, events and functions do not behave in a predictable way. There is no guarantee of exactly when an event will be handled, how long it will take to handle, or when an event generator will emit an event. Control flow is thus based on which events occur during its execution and in what order.

Event-based architecture styles are well suited to interactive applications. Graphical user interface applications that rely on user input and distributed systems that interact with other programs are prime candidates for this style.

Let us examine event-based architecture through an interactive game, “Cookie Clicker.” The goal of this game is to collect as many points as possible by clicking on an image of a cookie with your cursor. This can be done manually or by clicking on a cursor icon.

This icon has a functionality, it will purchase a special automatic clicker that automatically clicks the cookie at regular time intervals, thus reducing work on the user’s part to collect points. Users can “purchase” multiple cursors at the cost of some points.

Examine the component diagram below to see how this can be designed with an event-based architecture.

Implicit Invocation Systems-1791806267222 A main event loop listens for event. Depending on where users click within the window, a different event consumer will be invoked. When the cookie is clicked, the function registered to handle this click event is called and total points increases by one. When the “buy clicker” icon is clicked, then the function registered to handle this click event is called. This first reduces the score, as a purchase was made. Then, an automatic clicker function is added to the system, along with a timer function.

The timer function is an event generator, registered to the event bus, that sends a “timer event” every five seconds. The event bus detects when such an event is emitted and triggers every automatic clicker function to consume this event. Each automatic clicker function is responsible for making one click to the cookie. Every time a timer event is sent to the event bus and received by the automatic clicker functions, the total points increase by the number of automatic clicker functions. These event consumers, event generators, and event bus form the system of the game.

Built with LogoFlowershow