Common Design Patterns for Event-Driven Architecture

Sumit Bhattacharyya
8 min readJan 3, 2023

--

Event-driven architecture (EDA) is a design pattern in which the flow of an application is driven by events, rather than by a sequential series of instructions. In EDA, events are generated by various sources, such as user actions, sensor readings, or external API calls, and are then processed by one or more event processors.

Some Use Cases of EDA:

  1. Real-time processing: EDA can be used to build applications that need to process and respond to events in real-time, such as applications that monitor and control physical devices, or that provide instant feedback to users.
  2. Asynchronous processing: EDA can be used to build applications that need to process events asynchronously, such as applications that perform long-running tasks in the background or that handle high volumes of events.
  3. Microservices: EDA can be used to build applications using a microservices architecture, in which each service is designed to handle a specific set of events and can be independently deployed and scaled.
  4. Serverless computing: EDA can be used in conjunction with serverless computing platforms, which allow you to execute code in response to specific events without the need to provision and manage servers.
  5. Internet of Things (IoT): EDA can be used to build applications that connect and interact with IoT devices, which often generate events that need to be processed in real-time.

Event-driven architecture (EDA) can be used for data integration between multiple systems. In an event-driven data integration scenario, events are generated by one system and are then processed by another system, which may be responsible for storing, transforming, or forwarding the data to other systems.

Examples of EDA in Data Integration:

  1. Data synchronization: EDA can be used to build applications that synchronize data between two or more systems in real-time, such as applications that replicate data between a central database and multiple edge nodes.
  2. Data migration: EDA can be used to build applications that migrate data from one system to another, such as applications that transfer data from an on-premises system to the cloud.
  3. Data transformation: EDA can be used to build applications that transform data as it is being transferred between systems, such as applications that apply filters, aggregations, or transformations to the data.
  4. Data quality: EDA can be used to build applications that monitor and enforce data quality standards, such as applications that validate data as it is being transferred between systems or that flag invalid or inconsistent data.
  5. Data security: EDA can be used to build applications that enforce data security policies, such as applications that encrypt or mask data as it is being transferred between systems.

Common Architectural Patterns for EDA

There are several common architectural patterns that can be used when designing event-driven architecture (EDA) systems:

  1. Publish-subscribe: In a publish-subscribe architecture, event producers (also known as publishers) send events to an event broker, which then forwards the events to one or more event consumers (also known as subscribers). This pattern decouples the event producers from the event consumers, allowing them to operate independently and asynchronously.
  2. Event sourcing: In an event sourcing architecture, events are used to record the state changes of an application over time. This allows the application to be rebuilt by replaying the events, which can be useful for debugging, testing, or auditing purposes.
  3. Command-query responsibility segregation (CQRS): In a CQRS architecture, separate systems are used to handle commands (which mutate state) and queries (which retrieve state). This can be useful for building scalable systems that need to handle high volumes of read and write operations.
  4. Saga: A saga is a long-running process that is implemented as a series of small, independent transactions. Each transaction updates the state of the system and, if successful, triggers the next transaction in the saga. If any transaction fails, the saga is rolled back to a previous state. This pattern is useful for building applications that need to maintain consistency across multiple systems.
  5. Microservices: EDA can be used in conjunction with a microservices architecture, in which each service is designed to handle a specific set of events and can be independently deployed and scaled.

Useful design patterns for EDA

There are several design patterns that can be useful when designing event-driven architecture (EDA) systems:

  1. Observer pattern: The observer pattern is a behavioral design pattern that allows an object (the subject) to notify one or more other objects (the observers) when its state changes. This pattern is often used in EDA to allow event consumers to be notified when new events are available.
  2. Mediator pattern: The mediator pattern is a behavioral design pattern that allows objects to communicate indirectly, through a mediator object, rather than directly with each other. This pattern is often used in EDA to decouple event producers and event consumers and to reduce the number of dependencies between them.
  3. Command pattern: The command pattern is a behavioral design pattern that allows an object to represent an action or operation. This pattern is often used in EDA to encapsulate event payloads and to provide a standard interface for executing event-handling logic.
  4. Strategy pattern: The strategy pattern is a behavioral design pattern that allows an object to change its behavior at runtime by selecting from a set of predefined strategies. This pattern is often used in EDA to implement different event-handling logic based on the type or content of an event.
  5. Chain of responsibility pattern: The chain of responsibility pattern is a behavioral design pattern that allows an object to pass a request along a chain of objects until it is handled. This pattern is often used in EDA to build event processing pipelines that can route events to multiple event processors.
  6. The outbox pattern is a design pattern that can be used in event-driven architecture (EDA) systems to ensure the consistency of data between multiple systems. The outbox pattern works by storing a copy of each event that is produced by a system in a local outbox table. The event is then marked as pending, and the system continues to function as normal. A separate process, known as the outbox processor, is responsible for reading events from the outbox and delivering them to the appropriate event consumers. If an event fails to be delivered, the outbox processor can retry the delivery at a later time, using a backoff strategy to avoid overwhelming the event consumer.

The outbox pattern has several benefits:

  1. It allows the event producer to continue functioning even if the event consumer is temporarily unavailable.
  2. It decouples the event producer from the event consumer, allowing them to operate independently and asynchronously.
  3. It provides a record of all events that have been produced, which can be useful for debugging, testing, or auditing purposes.
  4. It allows the event producer to retry event delivery if necessary, which can be useful in cases where the event consumer is experiencing temporary issues or is processing events slowly.

Message Brokers for EDA

A message broker is a software that enables communication between different applications by passing messages between them. Here are some common message brokers and their main characteristics:

  • RabbitMQ: An open-source message broker that supports multiple messaging protocols. It is written in Erlang and has a good reputation for reliability.
  • Apache Kafka: An open-source message broker that is designed for high-throughput and low-latency processing. It is written in Java and is often used for building real-time streaming data pipelines.
  • Amazon Simple Queue Service (SQS): A cloud-based message broker offered by Amazon Web Services (AWS). It is a fully managed service that is easy to use and provides high availability and durability.
  • Google Cloud Pub/Sub: A cloud-based message broker offered by Google Cloud Platform. It is designed for large-scale data streaming and real-time event processing.
  • Microsoft Azure Service Bus: A cloud-based message broker offered by Microsoft Azure. It supports a variety of messaging patterns and has good integration with other Azure services.
  • Solace is a commercial message broker that supports a variety of messaging protocols and is designed for high-performance and high-scale environments. It offers a range of features, including support for publish/subscribe messaging, message queueing, and integration with popular cloud platforms. Solace is often used in financial services, gaming, and other industries where low latency and high throughput are important.

One notable feature of Solace is its support for “guaranteed messaging”, which means that it can ensure that messages are delivered even if the recipient is temporarily unavailable. This can be useful in scenarios where it is important to have a high level of reliability and where it is not acceptable for messages to be lost.

Overall, Solace is a powerful and feature-rich message broker that is well-suited for demanding environments. However, it is a commercial product and may not be the best choice for everyone, as it may be more expensive than some open-source alternatives.

There are many other message brokers available, and the right choice for you will depend on your specific requirements and constraints. Some things to consider when choosing a message broker include the level of scalability you need, the level of reliability required, the programming languages and platforms you are using, and whether you need a cloud-based or on-premises solution.

Push vs Pull model

It’s important to consider Push vs Pull models while sending messages from the broker to the receivers.

In a push model, the sender of a message actively sends the message to a receiver. The receiver does not need to take any action to receive the message; it is simply delivered to them. This is in contrast to a pull model, in which the receiver must actively request messages from the sender in order to receive them.

Push and pull models have different trade-offs and can be more or less appropriate depending on the specific situation. Here are some advantages and disadvantages of each:

Push model:

Advantages:

  • The sender has control over when messages are delivered, which can be useful in situations where timely delivery is important.
  • The receiver does not need to actively request messages, which can simplify their design.

Disadvantages:

  • The sender may need to buffer messages if the receiver is not ready to receive them, which can increase memory usage.
  • The receiver may not be able to handle a high volume of messages, leading to dropped messages or other performance issues.

Pull model:

Advantages:

  • The receiver has control over when it receives messages, which can be useful in situations where the receiver has limited resources or needs to process messages at its own pace.
  • The sender does not need to buffer messages, which can reduce memory usage.

Disadvantages:

  • The receiver must actively request messages, which can add complexity to its design.
  • The sender has less control over when messages are delivered, which can be a disadvantage in situations where timely delivery is important.

In general, the push model is better suited for situations where timely delivery is important and the receiver is able to handle a high volume of messages. The pull model is better suited for situations where the receiver has limited resources or needs to process messages at its own pace.

Conclusion:

Event-driven architecture is a way of building software systems that allows them to be more reactive and flexible. It is based on the idea that instead of having different parts of the system constantly checking for updates or new information, they can instead focus on their own tasks and only pay attention when something interesting happens.

Like any software design pattern, event-driven architecture has its own set of challenges and trade-offs. One potential challenge is that it can add complexity to the system, because you have to set up a way for the different components to communicate with each other and handle events. This can require more planning and design up front, and it can also make it more difficult to understand how the system works as a whole.

In this article I have highlighted some common design patterns, components and design choices to consider for tackling the complexity of building an event-driven software systems.

--

--

Sumit Bhattacharyya

Experienced and Passionate Software Architect adept in bringing forth expertise in design, development and maintenance of software systems.