Event-Driven Architecture

Autonomous Agents Intermediate autonomous-agent-skills universal
0 Upvotes
4 Views
0 Downloads
842 Words

Description

Design and implement event-driven architectures using pub/sub, event sourcing, message queues, and reliable event handlers.

When to Use

I want to design an event-driven system. | How do I implement pub/sub for scalable apps? | Explain event sourcing and durable event logs. | How can I handle eventual consistency across services?

Use Cases

Design decoupled services using event streams. | Implement durable event logs with Kafka or SQS. | Route messages with topic- and content-based filters. | Ensure idempotent event handlers across services.

SKILL.md Content

---
name: event-driven-architecture
description: "Design and implement event-driven architectures using pub/sub, event sourcing, message queues, and reliable event handlers."
metadata:
  tags: "event-driven, event-driven-architecture, pub-sub, event-sourcing, message-queues, distributed-systems, design-patterns"
  source: "https://skilldb.dev/skills/autonomous-agent-skills/event-driven-architecture"
  pack: "autonomous-agent-skills"
  category: "Autonomous Agents"
---

# Event-Driven Architecture

## When to use this skill
Use when the user says things like:
- "I want to design an event-driven system."
- "How do I implement pub/sub for scalable apps?"
- "Explain event sourcing and durable event logs."
- "How can I handle eventual consistency across services?"


You are an AI agent that designs and implements event-driven systems. You understand event flows, asynchronous communication patterns, and the trade-offs of decoupled architectures. You build systems where components communicate through events rather than direct calls.

## Philosophy

Event-driven architecture inverts the communication model. Instead of component A calling component B directly, A emits an event and B listens for it. This decouples producers from consumers, allows independent scaling, and makes it possible to add new behaviors without modifying existing code. The trade-off is increased complexity in debugging, ordering guarantees, and consistency.

Events represent facts — things that have already happened. They are immutable records of state changes. Designing around events means designing around the truth of what occurred, not the commands of what should occur.

## Techniques

### Event Emitters and Listeners

The simplest form of event-driven communication. A component emits named events with payload data, and registered listeners respond. This works well within a single process.

Key considerations: listener registration order may affect behavior; synchronous listeners block the emitter; unhandled errors in listeners can crash the emitter. Always decide whether listeners should be sync or async, and handle errors within each listener independently.

### Pub/Sub Patterns

Publish/subscribe extends event emitters across process and network boundaries. A message broker mediates between publishers and subscribers. Publishers do not know who subscribes; subscribers do not know who publishes.

Topic-based pub/sub routes messages by topic name. Content-based pub/sub routes based on message content matching subscriber-defined filters. Topic-based is simpler and more common.

Common brokers: Redis Pub/Sub for simple cases, RabbitMQ for routing flexibility, Apache Kafka for high-throughput durable streams, AWS SNS/SQS for managed cloud infrastructure.

### Event Sourcing Basics

Instead of storing current state, store the sequence of events that produced that state. The current state is derived by replaying events. This provides a complete audit trail, enables temporal queries ("what was the state at time T?"), and allows rebuilding state from scratch.

Event sourcing adds complexity: event schemas must evolve carefully, replay performance needs attention, and the read model often needs separate projections for query efficiency (CQRS pattern).

### Event Schema Design

Events should be self-describing and versioned. Include:

- Event type (a clear, past-tense name: `OrderPlaced`, `UserRegistered`, `PaymentFailed`)
- Event ID (unique identifier for deduplication)
- Timestamp (when the event occurred)
- Source (which component produced it)
- Version (schema version for evolution)
- Payload (the domain-specific data)

Keep payloads focused. Include the data consumers need, but avoid stuffing in entire entity snapshots unless you are doing event sourcing.

### Eventual Consistency

In event-driven systems, consistency between components is eventual, not immediate. After an event is published, there is a window where different components have different views of the truth. Design for this:

- Make UIs tolerant of stale reads
- Use idempotent operations so processing the same event twice is safe
- Implement compensation events for rollback rather than distributed transactions

### Dead Letter Handling

Messages that cannot be processed after repeated attempts go to a dead letter queue (DLQ). Implement monitoring and alerting on DLQ depth. Provide tooling to inspect, replay, or discard dead letters. Every production event system needs a dead letter strategy.

### Idempotent Event Handlers

Handlers must produce the same result whether they process an event once or multiple times. At-least-once delivery is the norm in distributed systems — exactly-once is extremely hard to guarantee. Store processed event IDs to detect duplicates, or design operations to be naturally idempotent (SET vs INCREMENT).

## Best Practices

- Name events in past tense — they represent things that happened, not commands
- Version your event schemas from the start and plan for backward compatibility
- Keep event payloads minimal but sufficient — avoid forcing consumers to make extra queries
- Implement dead letter queues and monitor them from day one
- Use correlation IDs to trace event chains across services
- Design all handlers to be idempotent — assume every event may arrive more than once
- Test event flows end-to-end, not just individual handlers in isolation
- Document the event catalog: which events exist, who produces them, who consumes them

## Anti-Patterns

- **The Event Soup**: Publishing dozens of fine-grained events for every state change, making it impossible to understand system behavior
- **The Synchronous Disguise**: Using events but then blocking until the consumer responds, negating the benefits of async communication
- **The Missing Schema**: Publishing events with no defined structure, leaving consumers to guess at payload shapes
- **The Ordered Assumption**: Writing handlers that assume events always arrive in order when the broker does not guarantee ordering
- **The God Event**: A single event type with a massive payload that every consumer must parse through
- **The Tight Coupling via Events**: Consumer logic that breaks when a producer changes internal event details, recreating coupling through the event contract
- **The Ignored DLQ**: Having a dead letter queue but never monitoring it, so failures silently accumulate
- **The Command Event**: Naming events as commands (`SendEmail`, `ProcessPayment`) — events describe what happened, not what should happen