DynamoDB Streams & EventBridge — Event-Driven Serverless
Why Event-Driven Serverless Matters
Event-driven architecture is the heart of serverless computing. Instead of polling or synchronous calls, services react to events as they happen. DynamoDB Streams captures every change to your database, and EventBridge routes events between services. Together they enable powerful real-time data pipelines.
Why this matters for your career:
- Event-driven architecture is essential for modern cloud-native applications
- DynamoDB Streams enables real-time data replication and processing
- EventBridge is AWS's serverless event bus — connects all AWS services
- Understanding event-driven patterns is key for AWS certification exams
DynamoDB Streams
DynamoDB Streams captures a time-ordered sequence of item-level changes in a DynamoDB table. Each stream record represents a single data modification.
Stream Record Structure
{
"eventID": "1",
"eventName": "INSERT", // INSERT | MODIFY | REMOVE
"eventVersion": "1.0",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"Keys": {
"id": {"S": "user_123"}
},
"NewImage": {
"id": {"S": "user_123"},
"name": {"S": "Alice"},
"email": {"S": "alice@example.com"},
"createdAt": {"S": "2025-01-15T10:30:00Z"}
},
"OldImage": null,
"SequenceNumber": "111",
"SizeBytes": 128,
"StreamViewType": "NEW_AND_OLD_IMAGES"
},
"eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/Users/stream/2025-01-01"
}
Stream View Types
| Type | Description | Use Case | |------|-------------|----------| | KEYS_ONLY | Only the key attributes of the modified item | Track which items changed | | NEW_IMAGE | The entire item after modification | Replicate data to another system | | OLD_IMAGE | The entire item before modification | Audit trail, undo operations | | NEW_AND_OLD_IMAGES | Both before and after images | Full change data capture |
Lambda + DynamoDB Streams Example
import json
import boto3
def lambda_handler(event, context):
"""Process DynamoDB Stream events."""
for record in event['Records']:
event_name = record['eventName'] # INSERT, MODIFY, REMOVE
if event_name == 'INSERT':
new_item = record['dynamodb']['NewImage']
user_id = new_item['id']['S']
email = new_item['email']['S']
print(f"New user created: {user_id} ({email})")
# Trigger welcome email via SES
send_welcome_email(email)
elif event_name == 'MODIFY':
new_image = record['dynamodb']['NewImage']
old_image = record['dynamodb'].get('OldImage', {})
print(f"User updated: {new_image['id']['S']}")
# Check for specific field changes
if new_image.get('email') != old_image.get('email'):
print("Email changed — send verification")
elif event_name == 'REMOVE':
old_item = record['dynamodb']['OldImage']
print(f"User deleted: {old_item['id']['S']}")
# Clean up related resources
cleanup_user_data(old_item['id']['S'])
return {'statusCode': 200}
def send_welcome_email(email):
ses = boto3.client('ses')
ses.send_email(
Source='welcome@example.com',
Destination={'ToAddresses': [email]},
Message={
'Subject': {'Data': 'Welcome to our platform!'},
'Body': {'Text': {'Data': 'Thank you for signing up...'}}
}
)
def cleanup_user_data(user_id):
# Delete related records in other tables
dynamodb = boto3.resource('dynamodb')
orders_table = dynamodb.Table('Orders')
response = orders_table.query(
IndexName='UserIdIndex',
KeyConditionExpression=boto3.dynamodb.conditions.Key('userId').eq(user_id)
)
with orders_table.batch_writer() as batch:
for item in response['Items']:
batch.delete_item(Key={'id': item['id']})
Amazon EventBridge
EventBridge is a serverless event bus that connects application data from your own apps, AWS services, and third-party SaaS applications.
EventBridge vs. SNS
| Feature | EventBridge | SNS | |---------|-------------|-----| | Schema | Structured event with detail-type | Generic message | | Filtering | Content-based filtering (rules) | Topic-based only | | Targets | Multiple targets per rule | Subscribers to topic | | Third-party integration | Built-in (Zendesk, Shopify, etc.) | Custom integration needed | | Schema registry | ✅ Yes | ❌ No | | Replay events | ✅ Yes | ❌ No | | Archive events | ✅ Yes | ❌ No |
EventBridge Rules Example
{
"source": ["myapp.users"],
"detail-type": ["UserCreated", "UserUpdated"],
"detail": {
"plan": ["premium", "enterprise"]
}
}
This rule matches events where:
- source = "myapp.users"
- detail-type is "UserCreated" or "UserUpdated"
- The user's plan is "premium" or "enterprise"
Lambda + EventBridge Example
import json
import boto3
def create_user(event, context):
"""Create a user and emit an event."""
body = json.loads(event['body'])
# Save to DynamoDB
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Users')
table.put_item(Item={
'id': body['id'],
'name': body['name'],
'email': body['email'],
'plan': body.get('plan', 'free')
})
# Emit EventBridge event
eventbridge = boto3.client('events')
eventbridge.put_events(Entries=[{
'Source': 'myapp.users',
'DetailType': 'UserCreated',
'Detail': json.dumps({
'userId': body['id'],
'email': body['email'],
'plan': body.get('plan', 'free')
}),
'EventBusName': 'default'
}))
return {
'statusCode': 201,
'body': json.dumps({'message': 'User created', 'id': body['id']})
}
Scheduled Events (Cron)
{
"schedule": "rate(24 hours)",
"targets": [{
"arn": "arn:aws:lambda:us-east-1:123456789012:function:cleanup-expired-sessions",
"input": "{\"type\": \"cleanup\"}"
}]
}
Or use cron syntax:
schedule: "cron(0 2 * * ? *)" # Run at 2 AM every day
Common Event-Driven Patterns
| Pattern | Services | Use Case | |---------|----------|----------| | Change Data Capture (CDC) | DynamoDB Streams → Lambda → Elasticsearch | Real-time search indexing | | Fan-out | EventBridge → Multiple Lambdas + SQS + SNS | Notify multiple downstream systems | | Scheduled tasks | EventBridge (cron) → Lambda | Daily reports, cleanup jobs | | Choreography | SQS → Lambda → DynamoDB → SQS → Lambda | Distributed sagas, workflows | | Log aggregation | CloudWatch → Lambda → Elasticsearch | Centralized logging | | Throttle buffer | API Gateway → SQS → Lambda | Handle traffic spikes gracefully | | Dead letter queue | Lambda → DLQ (SQS) | Capture failed invocations |
Best Practices
| Practice | Reason | |----------|--------| | Enable streams on DynamoDB tables | Required for CDC and event-driven processing | | Use NEW_AND_OLD_IMAGES | Full audit trail of all changes | | Filter events in EventBridge rules | Lambda only runs for relevant events | | Set appropriate batch sizes | Optimize Lambda invocation frequency | | Handle duplicate events | Streams guarantee at-least-once delivery | | Use DLQ for failed events | Never lose data from processing failures | | Monitor iterator age | Detect slow stream processing | | Archive important events | EventBridge can replay archived events |
Summary
DynamoDB Streams and EventBridge enable powerful event-driven serverless architectures. DynamoDB Streams captures every data change in real-time. EventBridge routes events between services with content-based filtering and scheduled triggers. Together they form the backbone of modern event-driven applications on AWS.
Key takeaways:
- DynamoDB Streams captures INSERT, MODIFY, REMOVE events
- Stream view types: KEYS_ONLY, NEW_IMAGE, OLD_IMAGE, NEW_AND_OLD_IMAGES
- EventBridge is a serverless event bus with content-based filtering
- EventBridge supports scheduled events (cron/rate expressions)
- EventBridge has built-in integration with 200+ AWS services and SaaS apps
- Use DynamoDB Streams for real-time data replication and CDC
- Use EventBridge for decoupled, event-driven microservices
- Always implement idempotent processing for at-least-once delivery
What's Next: Step Functions
The next chapter covers AWS Step Functions — orchestrating multi-step serverless workflows with state machines, error handling, and parallel execution.