Design and implement event-driven cloud architectures with Kafka topics, Dapr pub/sub, and scalable microservice patterns...
Design scalable, loosely coupled event-driven architectures for cloud-native applications.
┌─────────────────────────────────────────────────────────────┐
│ Services │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Todo │ │ User │ │ Notify │ │Analytics│ │
│ │ Service │ │ Service │ │ Service │ │ Service │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │ │
│ └────────────┴────────────┴────────────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ Dapr │ ← Cloud-agnostic abstraction│
│ │ Sidecar │ │
│ └────┬────┘ │
└─────────────────────────┼───────────────────────────────────┘
│
┌─────┴─────┐
│Event Bus │ ← Kafka / Redis / Cloud PubSub
└───────────┘
<domain>.<entity>.<action>
Examples:
todos.task.created
todos.task.completed
users.account.registered
{
"specversion": "1.0",
"id": "uuid",
"source": "/todos/api",
"type": "todos.task.created",
"time": "2024-01-15T10:30:00Z",
"data": {
"todoId": "123",
"userId": "456",
"title": "Buy groceries"
}
}
User creates todo
│
▼
┌──────────────┐ todos.task.created
│ Todo Service │─────────────────────────┐
└──────────────┘ │
▼
┌────────────────────────────────────────┐
│ Event Bus │
└────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Notification │ │ Analytics │
│ Service │ │ Service │
└──────────────┘ └──────────────┘
from dapr.clients import DaprClient
async def publish_todo_created(todo: Todo):
with DaprClient() as client:
client.publish_event(
pubsub_name="pubsub",
topic_name="todos.task.created",
data=json.dumps({"todoId": str(todo.id), "title": todo.title})
)
@app.get("/dapr/subscribe")
async def subscribe():
return [
{"pubsubname": "pubsub", "topic": "todos.task.created", "route": "/events/todo-created"}
]
@app.post("/events/todo-created")
async def handle_todo_created(request: Request):
event = await request.json()
await send_notification(event["data"]["userId"], "Todo created!")
return {"status": "SUCCESS"}
# components/pubsub.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
type: pubsub.kafka # or pubsub.redis for local dev
version: v1
metadata:
- name: brokers
value: "kafka:9092"
Ensure event publishing doesn't fail separately from database writes:
BEGIN;
INSERT INTO todos (...) VALUES (...);
INSERT INTO outbox (event_type, payload) VALUES ('todos.task.created', '...');
COMMIT;
-- Separate relay publishes from outbox to Kafka
@app.post("/events/todo-created")
async def handle_todo_created(request: Request):
event = await request.json()
event_id = event["id"]
if await is_already_processed(event_id):
return {"status": "SUCCESS"} # Skip duplicate
await process_event(event["data"])
await mark_processed(event_id)
return {"status": "SUCCESS"}
@app.post("/events/todo-created")
async def handle_event(request: Request):
try:
await process_event(await request.json())
return {"status": "SUCCESS"}
except RetryableError:
return {"status": "RETRY"} # Dapr retries
except Exception:
return {"status": "DROP"} # Send to DLQ
| Pattern | Use For | Example |
|---|---|---|
| Events (async) | Notifications, analytics, sync | Todo created → notify |
| HTTP (sync) | Auth checks, validation | Verify user exists |
| Saga | Multi-step transactions | Order → Payment → Ship |