Getty Images
Event-driven vs. message-driven: It comes down to complexity
While the goal doesn't change, how do message-driven and event-driven design approaches really differ? More importantly, how do you choose the right approach?
The terms event-driven and message-driven often appear in conversations and lectures about communications between software systems. But besides the names, how do these two approaches actually differ? Furthermore, how do you decide which one is the right method to reach your goals without introducing new points of failure?
It's likely that, in today's world of distributed computing, software teams will need to tackle both methods of application communication. This makes it not just important to understand both approaches, but understand where they do and don't fit in. Let's begin by examining the major differences between a message-driven vs. event-driven approach to service communication, and then review some of the factors that dictate the right course of action.
The message-driven approach
Modern software systems are distributed systems that run across many endpoints and connect via large networks. For example, consider an airline customer buying a ticket through a web browser. That order might go through an API, and then through a series of processes that return a result. One term for these back-and-forth communications is messaging.
In a messaging architecture, these API calls look very much like a function call: The API knows what it is calling, expects a certain outcome and will wait for that result. When famed computer scientist Alan Kay refined the concept of object-oriented programming, he focused on messaging as a part of his definition. A message, according to Kay, should contain the following four elements:
- The identity of the object that receives the call;
- the code to be executed by the recipient;
- the arguments for the code; and
- the returned value.
The event-driven approach
Many times, however, the interactions between services and application components are much more complex. For instance, if a pilot is unable to complete the final leg of a flight, the airline may need to re-route planes, find a new flight crew and deal with customers who've had their tickets cancelled and rebooked. This might even involve issuing meal and hotel vouchers to passengers stuck waiting overnight for their flight through the airline's ticketing system or via a mobile app.
We can refer to these complex series of processes and transactions as events. When a system registers an event, it simply adds a note to a messaging queue -- or a messaging bus. Concerned systems can access these messages, process the associated event and, if needed, look through old events.
For instance, an event-driven user interface has no formal flow control. Instead, objects like buttons, menus and images fire notifications when certain events happen. Clicking a button might call the onBtnCommand() method; mousing over an image might call onImgHover() method. The computer then waits for a particular event to happen, then runs code associated with that event.
Likewise, in an event-driven architecture, a program notifies interested software components that something happened. The event-driven system is asynchronous and non-blocking: Drop the message, move on, and (maybe) pick up a confirmation message later.
A message-driven system example
In a service-oriented front-end architecture (SOFEA) web design approach, web pages are defined in HTML and CSS, but functional data is retrieved separately from the server. For example, front-end Javascript code could make an API call that requests a function and waits for the result.
The API call might ask for a user ID, an email address or whatever other elements of the user profile it requires. Similar API calls could ask for things like the results of a user search, a particular product page or the current contents of a shopping cart. While the web page can still display the general interface to the user waiting, it won't be able to show the number or list of items in the shopping cart until the function call returns with the data.
In a case like this, the web browser works as the messaging system. Authentication may be done through headers, open source authentication APIs or hosted options -- such as Google Identity or Facebook Login. The messages are formatted for REST and HTTP.
Event-driven system example
While actions like product searches, product displays and retrievals of shopping carts are often simple, one-to-one communications, an ordering process requiring a credit card transaction may not be. When that transaction goes through, that will need to reflect in an accounting system, a data warehouse and whatever physical warehouse needs to prepare the order. The company may also have an entire set of conditions to track the package, such as "received," "assembled," "delivered," etc.
In the case of an ordering process, an API could drop an event notification into a centralized enterprise service bus (ESB). The subsystems plugged into that ESB subscribes to those event notifications, so it is informed when a new event appears. When a warehouse system marks a product as "assembled," the inventory system adjusts itself accordingly, while the tracking system can now pick up the event and pass updates in the form of messages.
Event-driven vs. message-driven: How to choose
The message-driven approach has as many pros and cons as the event-driven approach, but each have their own cases where they are the best fit.
Messages feel very much like classic programming models: call a function, wait for a result, do something with the result. Along with being familiar to most programmers, this structure can also make debugging more straightforward. Another advantage is that messages "block," which means individual units of calls and responses sit and wait for their turn for processing by the recipient. Events, on the other hand, simply make their mark on the queue and allow the recipient to address it at its convenience, meanwhile moving on to the next call in the chain. This adds a lot of flexibility and speed when it comes to managing and executing unruly amounts of transactions.
Event-driven systems make the individual events easy to test in isolation. However, this separation from the overall application system also inhibits the ability for these units to report errors, retry call procedures or even just confirm to the user that a process is complete. In other words: When an error occurs in an event-driven system, it can be difficult to track down just what went wrong. Observability tools are rising to the challenge of debugging complex event chains. However, each tool added to the intersection point of a business transaction creates another layer of complexity for programmers responsible for managing these workflows.
If communications generally occur in a one-to-one manner, and receiving regular status updates or confirmations is a priority, you'll want to lean towards the message-based approach. However, if the interactions between systems are particularly complex, and the delays caused by confirmations and status updates make waiting for them unrealistic, an event-driven design might be more appropriate. Keep in mind, however, that most large organizations will end up with a hybrid strategy, with some customer-facing/API calls using messages and the enterprise itself using events. As such, it never hurts to familiarize yourself as much as possible with both.