AgentSkillsCN

event-driven-architect

利用 Kafka 主题、Dapr 发布/订阅机制以及可扩展的微服务模式,设计并实现事件驱动的云架构。适用于分布式系统的设计、异步消息传递的实现、基于事件的工作流构建,以及松耦合服务的开发。涵盖容错性强、云端可移植的架构设计。

SKILL.md
--- frontmatter
name: event-driven-architect
description: Design and implement event-driven cloud architectures with Kafka topics, Dapr pub/sub, and scalable microservice patterns. Use when designing distributed systems, implementing async messaging, creating event-based workflows, or building loosely coupled services. Covers fault-tolerant, cloud-portable architectures.

Event-Driven Architect

Design scalable, loosely coupled event-driven architectures for cloud-native applications.

Core Principles

  1. Loose coupling - Services communicate via events, not direct calls
  2. Fault tolerance - Failures don't cascade; retry and compensate
  3. Cloud portability - Abstract messaging via Dapr; switch brokers without code changes
  4. Event sourcing mindset - Events are the source of truth

Architecture Overview

code
┌─────────────────────────────────────────────────────────────┐
│                         Services                             │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐        │
│  │  Todo   │  │  User   │  │ Notify  │  │Analytics│        │
│  │ Service │  │ Service │  │ Service │  │ Service │        │
│  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘        │
│       │            │            │            │              │
│       └────────────┴────────────┴────────────┘              │
│                         │                                    │
│                    ┌────┴────┐                              │
│                    │  Dapr   │  ← Cloud-agnostic abstraction│
│                    │ Sidecar │                              │
│                    └────┬────┘                              │
└─────────────────────────┼───────────────────────────────────┘
                          │
                    ┌─────┴─────┐
                    │Event Bus  │  ← Kafka / Redis / Cloud PubSub
                    └───────────┘

Event Design

Naming Convention

code
<domain>.<entity>.<action>

Examples:
  todos.task.created
  todos.task.completed
  users.account.registered

Event Schema (CloudEvents)

json
{
  "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"
  }
}

Todo Event Workflow

code
User creates todo
       │
       ▼
┌──────────────┐     todos.task.created
│ Todo Service │─────────────────────────┐
└──────────────┘                         │
                                         ▼
                    ┌────────────────────────────────────────┐
                    │              Event Bus                  │
                    └────────────────────────────────────────┘
                         │                    │
                         ▼                    ▼
               ┌──────────────┐     ┌──────────────┐
               │ Notification │     │  Analytics   │
               │   Service    │     │   Service    │
               └──────────────┘     └──────────────┘

Dapr Pub/Sub (Recommended)

Publish Event

python
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})
        )

Subscribe to Events

python
@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"}

Component Config

yaml
# 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"

Key Patterns

Transactional Outbox

Ensure event publishing doesn't fail separately from database writes:

sql
BEGIN;
  INSERT INTO todos (...) VALUES (...);
  INSERT INTO outbox (event_type, payload) VALUES ('todos.task.created', '...');
COMMIT;
-- Separate relay publishes from outbox to Kafka

Idempotent Consumer

python
@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"}

Dead Letter Queue

python
@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

Service Communication

PatternUse ForExample
Events (async)Notifications, analytics, syncTodo created → notify
HTTP (sync)Auth checks, validationVerify user exists
SagaMulti-step transactionsOrder → Payment → Ship

Resilience Checklist

  • Idempotent event handlers (handle duplicates)
  • Dead letter queues for failed events
  • Circuit breakers for sync calls
  • Retry with exponential backoff
  • Graceful degradation (return defaults on failure)
  • Health checks for all services

References