066
architectural behavioral integration

Event-Driven Architecture

Reference Wikipedia ↗
Event-Driven Architecture — sequence diagram
Plate 066 sequence diagram

Event-Driven Architecture (EDA) is a software architecture paradigm where the flow of an application is determined by events. Instead of a traditional request-response model, components communicate by producing and consuming events. This promotes loose coupling, scalability, and responsiveness. Events represent a significant change in state, and components react to these events asynchronously.

EDA is particularly useful in distributed systems, microservices architectures, and applications requiring real-time processing. Common use cases include logging, monitoring, data pipelines, user interface updates, and integrating disparate systems. It allows for building highly scalable and resilient applications where components can fail independently without bringing down the entire system.

Usage

EDA is widely used in modern software development for:

  • Microservices Communication: Services publish events when their state changes, allowing other services to react without direct dependencies.
  • Real-time Data Processing: Applications like fraud detection or stock trading rely on immediate responses to events.
  • Decoupled Systems: Integrating systems with different technologies and lifecycles without tight coupling.
  • IoT Platforms: Handling streams of data from numerous devices.
  • Serverless Computing: Functions are triggered by events, enabling pay-per-use scaling.

Examples

  1. Kafka: Apache Kafka is a distributed streaming platform often used as an event bus in EDA. Producers write events to Kafka topics, and consumers subscribe to those topics to receive and process events. It’s used by Netflix for real-time monitoring and LinkedIn for activity tracking.

  2. AWS EventBridge: A serverless event bus service that makes it easier to build event-driven applications at scale. It allows you to route events between AWS services, SaaS applications, and your own custom applications. Many AWS customers use EventBridge to connect their services and automate workflows.

  3. Node.js EventEmitter: A core module in Node.js that provides a simple event handling mechanism. Components can emit events, and other components can listen for and respond to those events. This is a foundational pattern for building asynchronous and reactive applications in Node.js.

Specimens

15 implementations
Specimen 066.01 Dart View specimen ↗

The Event-Driven Architecture (EDA) decouples components by having them communicate through events. Components (event producers) emit events when something significant happens, and other components (event consumers) react to those events without needing direct knowledge of the producers. This promotes flexibility and scalability.

This Dart implementation uses StreamController to manage event streams. The EventBus class acts as a central hub for publishing and subscribing to events. Producers call publish() with an event object. Consumers subscribe to specific event types using stream.listen(). Dart’s asynchronous stream handling with StreamController and Stream is a natural fit for EDA, allowing for non-blocking event processing. The use of a dedicated EventBus class encapsulates the event management logic, keeping components clean and focused.

// event_bus.dart
import 'dart:async';

class EventBus {
  final _controller = StreamController<dynamic>();

  Stream<T> stream<T>() {
    return _controller.stream.map((event) => event as T);
  }

  void publish<T>(T event) {
    _controller.add(event);
  }

  void close() {
    _controller.close();
  }
}

// main.dart
import 'event_bus.dart';

class Counter {
  int _count = 0;

  void increment() {
    _count++;
    eventBus.publish(CounterIncrementedEvent(_count));
  }

  int get count => _count;
}

class CounterIncrementedEvent {
  final int newCount;
  CounterIncrementedEvent(this.newCount);
}

void main() {
  final eventBus = EventBus();

  final counter = Counter();

  eventBus.stream<CounterIncrementedEvent>().listen((event) {
    print('Counter incremented to: ${event.newCount}');
  });

  counter.increment();
  counter.increment();

  eventBus.close();
}