EDA: Enterprise Integration Patterns

June 7, 2024

Multiples patterns exists, multiple patterns are possible. Theses patterns aren’t mutually exclusive, there are often used together in a same stack.

Common Event Patterns

When building an Event Driven Application, we generally think of asynchronous tasks. This doesn't need to be the case. You can have a request/response model using events that behaves synchronously. This is generally achieved by polling a resource to read the result, or by the use of websockets listing to the response.

Synchronous Request Response Model (Blocking Model)

This model follows a Send → Wait → Receive sequence. The sender initiates the request and has to wait for the process to end to be able to free the connection, hence it is often referred to as a blocking model.

This kind of model is often used in web application with a server side processing component, like PHP or Ruby on Rails.

At first glance, there doesn't seem to be any event in this scenario. Let drill into the details of a form submission from a web application towards an API Gateway integrated with Lambda and DynamoDB:

  • A User fills out a form on a website
  • The User submits the form
  • The Browser initiates a POST request towards our HTTP endpoint
  • API Gateway notifies the Lambda Service of a new request. This is an event containing the payload and the Lambda Function to execute.
  • The Lambda Service executes the Lambda function
  • The Lambda function calls the DynamoDB endpoint to store the payload. This is an event inside the NodeJS application.
  • The DynamoDB Service will use events to ensure that the data is successfully stored in multiple AZs
  • Our code awaits the DynamoDB response
  • Our function returns the response to the Lambda Service
  • The Lambda Service returns the response to API Gateway
  • API Gateway returns the response to the Browser
Submitting a form synchronously: The browser waits for the backend to finish processing and returning a response

With this approach, the Browser has to wait for the full process to finish. API Gateway needs to wait for Lambda to reply. Lambda needs to wait for DynamoDB to reply. During this waiting time, for the User submitting the form, nothing seems to happen. His browser will display a blank page until the response is returned.

In modern web applications, the waiting process is hidden to the User with the use of Javascript and well thought spinners and/or modals.

When to use this model?

When the client needs a response directly and on short executions. The example of a database insert fits this model. The Browser needs to know if the insert was successful, to be able to inform the User. This execution is short lived unless complex verification are in place.

When not to use this model?

When the execution time is long. Expanding on the form submission example, let’s add an external validation:

  • The Lambda function calls an external API
  • On Success, the data is stored in the database
  • On Error, an error is returned to the client

A more complex synchronous form submission: The backend process needs to call a 3rd party service before storing to the database

Asynchronous Point to Point Model (Queue)

This model follows a Send → Queue → Consumer model. The sender receives a response that his request will be honored and the request is sent to a queue for asynchronous processing.

This model is an extension on the previous model. The Browser doesn’t need to wait for the 3rd party call to be finished. The backend application acknowledges the reception of the payload and will process it asynchronously, terminating the initial call from the Browser.

With this solution, we are not only able to control the request rate made to the 3rd party endpoint, but we can also retry failed requests to the 3rd party in case of transient failures.

A new endpoint needs to be added to allow the browser to inquire and retrieve the result of the task.

By processing the payload asynchronously, the browser connection can be released earlier. We have control over the 3rd party requests throttling.A new endpoint needs to be provided to allow the browser to retrieve the status of the initial request.

When to use this model?

  • Need to throttle requests to a downstream service
  • Need to retry failed requests to a downstream service

When not to use this model?

  • A synchronous response is need from the downstream service

Asynchronous Point to Point Model (Router)

With this model, the sender maintains the routing logic. The sender determines which message is intended for a specific consumer. The message are formatted for a specific consumer.

This model is well suited for communication inside a stack, where the number of consumers is small and the payload format is well known.

This model can be implemented with the use of Amazon SNS. Every event produced is pushed to SNS. All subscribed consumers will receive the message as formatted by the sender.

An example of such an integrations is a webhook integration with an alarm service.

When to use this model?

  • When there is a small amount of well known consumers
  • When all consumers use the same format
  • Events are processed only once and no throttling is needed

When not to use this model?

  • When consumers are not identifiable or not a discrete and manageable amount
  • When multiple consumers need different payloads
  • When retry and/or throttling is needed

Asynchronous Point to Point Model (Bus)

This model follows a Sender → Bus model. Multiple receivers can subscribe to the bus and receive messages matching a specific filter.

This model is well suited to communicate between stacks, services, departments or even enterprises. It is generally the model that is thought of when speaking of Event Driven Applications.

The sender doesn’t need to know who is subscribed. The events are not formatted for a specific receiver, but rather from the sender’s point of view.

In the AWS ecosystem, Amazon EventBridge or Amazon Managed Kafka (MSK) are the services of choice.

After a successful object upload to S3, the S3 service sends an event to EventBridge. Our Lambda function subscribes to all event for object created with the prefix “uploads/” and will ignore objects stored in another bucket or prefix.

When to use this model?

  • Multiple services need to act on something that happened in the sender’s service
  • The sender service doesn’t need to be concerned with what happens downstream

When not to use this model?

  • The sender needs to know the result of a downstream service
    • This could be overcome with events sent back from the downstream service
  • The receiver process needs to be invoke synchronously

Some implementations in the world

Applications notifications

Any mobile or web application will use events to notify their users. Be it a messenger style app,  news app or even games.

You post a new message on Facebook, Facebook triggers an event that a new message from userA has been posted. This event is then processed asynchronously, the process fetches the list of friends of userA. Which creates a new event for userA’s friends: userB and userC.

Theses events are picked up by the notification process that will send the message to userB and userC.

By using events, the notion of “posting a new message” becomes independent from “sending a notification”. UserA doesn’t need to know who is subscribed to his feed.

At the same time, userB can receive notifications from other feeds he is subscribed to: userA, userD, userE without adding a specific integration.

Chatbots

The user sends a message or request to and endpoint. This endpoint will invoke through an event a chatbot service built on an LLM stack. The LLM’s response will be returned to the user.

In this case, we have a synchronous (blocking) event flow. The initial user’s request needs to wait for the response. Internally, the services used (gateway, validation, LLM) are invoked by events.

Webhooks

Webhooks as the name implies are http based transfers and are used to transfer events between two independent systems. An event on systemA triggers a webhook call to systemB. Upon reception in systemB a new event is created and systemB can act upon it.

As an example, we can look at Github: you can define a webhook for all “push” events. When a push event is happening in your repository, an http request to the webhook endpoint is made.

Your endpoint will parse the request and either create an event or directly process it. Generally we want to create a new event to free quickly the incoming request.

Siri, Alexa, Google Home

Theses devices transform a voice command into an event. The event is being processed by the concerned application on the device, which makes a request to the relevant service. The response is creates a new event, that is in turn processed by the device and transformed back into voice.

As an example, when telling Alexa “ask OurGrocieries to add milk”, the keyword “ask” tells Alexa the the event is intended to a 3rd party app, “OurGroceries” being this application.

Alexa processed this event and sends it to the webhook provided by the integration. The integration knows what to do with the payload “add Milk”.

In this example, Alexa stops the processing as soon as the webhook is called. It doesn’t need to wait for a response.

Internet of Things (IoT)

This is very similar to the “applications notifications” use case. But instead of displaying a notification, the IoT device will execute a process in reaction to the event.

Let’s look at a simple case of a connected light bulb. The light bulb’s address is known by a controller unit. This controller unit is linked to Alexa.

A user initate the command “Alexa, turn on livingroom”. This action is already an event, Alexa received the event “turn on” with the payload “livingroom”. The light integration listens to all events “turn on”, it will process this event, and ignore events like “play”.

“Livingroom” is probably a collection of light bulbs, we configured them as such in Alexa. The process acting on the event, will list all light bulbs in this group and send a notification via a webhook to the controller for each light bulb. Either in batch (turn on: lb1, lb2, lb3), or individually.

The controller will create an event for each light bulb and send via the Zigbee protocol the command to each bulb. Upon receiving the event, the bulb turns on.

In this flow, events are used to decouple multiple technologies. First we have a speech to text recognition, then an HTTP transport and finally a Zigbee transport. Alexa doesn’t need to implement Zigbee. The bulb doesn’t need to implement http.

By using events, we are able to have more than one source to control our lights. You can control them via an app, via a movement detector or another smart home device.

You can also control them via a physical remote, in this case the remote directly talks to the bulb. In return the bulb will send an event to the controller to inform it of its new state.

Multiple trigger sources can interact with multiple IoT devices across protocols

Database triggers

In this scenario, an action is taken when items in the database are modified. This can be synchronous or asynchronous.

As an example, in an SQL database, a value is updated in another table before each insert. This a blocking event, since the event is happening “before insert”.

  
CREATE OR REPLACE TRIGGER Update_Total
  BEFORE INSERT ON Sales_tab
  FOR EACH ROW
  WHEN (new.amount > 0)
  BEGIN
    UPDATE Total_tab SET total = total + new.amount WHERE category = new.catagory
  END;
  

An event happening after insert, can be asynchronous. This is the case with Amazon DynamoDB. Using DynamoDB Streams, all actions on a table are sent to a stream. The stream is the source for events on which a new action can be executed.

DynamoDB sends CRUD events asynchronously to DynamoDB Streams, which can be consumed by multiple destinations.

In theses cases, events are used to decouple the user action with database actions. The user or application interacting with the database doesn’t need to know what is happening at the database level. For asynchronous tasks, the caller isn’t impacted by the latency that additional operations are creating.

Change Data Capture (CDC)

In this scenario, we want to publish any data change in the database to be consumed by any client. As you probably guessed it, this is an extension on the “Database Trigger” model. Instead of updating a table, we can simply publish the event (or DynamoDB stream) to EventBridge.

Serverless Handbook
Access free book

The dream team

At Serverless Guru, we're a collective of proactive solution finders. We prioritize genuineness, forward-thinking vision, and above all, we commit to diligently serving our members each and every day.

See open positions

Looking for skilled architects & developers?

Join businesses around the globe that trust our services. Let's start your serverless journey. Get in touch today!
Ryan Jones - Founder
Ryan Jones
Founder
Speak to a Guru
arrow
Edu Marcos - CTO
Edu Marcos
Chief Technology Officer
Speak to a Guru
arrow
Mason Toberny
Mason Toberny
Head of Enterprise Accounts
Speak to a Guru
arrow

Join the Community

Gather, share, and learn about AWS and serverless with enthusiasts worldwide in our open and free community.