arrow_backBack to Knowledge Base
Backend Architecture·Jul 18, 2025·15 MIN READ

Building Event-Driven Systems with Lambda, SQS & EventBridge

Why synchronous APIs don't scale. A deep dive into decoupling services, dead-letter queues, idempotency, and retry strategies.

lambdasqseventbridgearchitecture

01.The Synchronous Bottleneck

When Service A calls Service B synchronously, you've created a temporal coupling. If B is slow, A is slow. If B is down, A fails. This is fine for simple CRUD — it's catastrophic for document processing, payment workflows, or anything with unpredictable latency. Event-driven architecture decouples producers from consumers entirely.

02.SQS vs EventBridge — When to Use Which

SQS is for task queues — one consumer, guaranteed processing, retry logic baked in. EventBridge is for event routing — many consumers, fan-out patterns, rule-based filtering. Use SQS when you need guaranteed delivery and exactly-once semantics. Use EventBridge when multiple services need to react to the same event.

  • SQS: document processing, email sending, async jobs
  • EventBridge: system-wide events like 'deal.created', 'user.signup'
  • SQS visibility timeout: set to 6x your Lambda timeout
  • EventBridge rules: filter by detail-type to route events cleanly

03.Dead Letter Queues Are Not Optional

Every SQS queue must have a DLQ. Set maxReceiveCount to 3 — after 3 failed processing attempts, the message moves to the DLQ. Set up a CloudWatch alarm on DLQ depth. When the alarm fires, you get a PagerDuty/SNS notification. Without this, messages silently disappear and you never know what failed.

typescript
// CDK example
const dlq = new sqs.Queue(this, 'ProcessingDLQ', {
  retentionPeriod: Duration.days(14),
});
const queue = new sqs.Queue(this, 'ProcessingQueue', {
  visibilityTimeout: Duration.seconds(300),
  deadLetterQueue: { queue: dlq, maxReceiveCount: 3 },
});

04.Idempotency — Handle Duplicate Messages

SQS guarantees at-least-once delivery, meaning duplicates are possible. Your Lambda handlers must be idempotent. Use a DynamoDB or Redis idempotency key (typically the messageId) to detect and skip already-processed messages. This prevents double-charging, double-processing, and data corruption.

05.Structured Event Schema

Every event should follow a consistent envelope schema. This makes debugging, logging, and routing predictable across all services. Never publish raw data blobs — always include metadata about the event's origin, version, and timestamp.

typescript
interface DomainEvent<T> {
  eventId: string;       // UUID for idempotency
  eventType: string;     // 'document.classified'
  version: string;       // '1.0'
  timestamp: string;     // ISO 8601
  source: string;        // 'extraction-service'
  payload: T;
}