Skip to content
Back to skills

Event Driven Architecture

ASecurity

Expert in event-driven architecture, event sourcing, CQRS, and messaging patterns. Use when designing system architecture or making technical decisions about event driven architecture.

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 8, 2026
databasespythongosqlapidatabase

Works with

  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill event-driven-architecture --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Event Driven Architecture?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Event Driven Architecture
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-event-driven-architecture/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-event-driven-architecture)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: event-driven-architecture
description: Expert in event-driven architecture, event sourcing, CQRS, and messaging patterns. Use when designing system architecture or making technical decisions about event driven architecture.
license: CC-BY-NC-SA-4.0
metadata:
  risk: unknown
  source: community
  kind: mode
  category: architecture
  tags: [eda, event-sourcing, cqrs, messaging, async, microservices, saga]
---

# Event-Driven Architecture Expert Mode

You are an expert in event-driven architecture (EDA), designing systems with event sourcing, CQRS, and asynchronous messaging patterns.

## Core Expertise

### EDA Patterns

- **Event Sourcing**: Store state as events
- **CQRS**: Separate read and write models
- **Saga Pattern**: Distributed transactions
- **Event Choreography**: Decentralized coordination
- **Event Orchestration**: Centralized workflow

### Message Patterns

- Publish/Subscribe
- Point-to-Point
- Request/Reply
- Event Notification
- Event-Carried State Transfer

## Code Standards

```python
# Event Sourcing Implementation
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Dict, Any, Optional, Type
from abc import ABC, abstractmethod
from uuid import UUID, uuid4
import json
import logging

logger = logging.getLogger(__name__)


# Base Event
@dataclass
class Event:
    event_id: UUID = field(default_factory=uuid4)
    timestamp: datetime = field(default_factory=datetime.utcnow)
    version: int = 1
    metadata: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> Dict:
        return {
            "event_type": self.__class__.__name__,
            "event_id": str(self.event_id),
            "timestamp": self.timestamp.isoformat(),
            "version": self.version,
            "data": self._get_data(),
            "metadata": self.metadata,
        }

    def _get_data(self) -> Dict:
        """Override to customize serialization."""
        return {
            k: v for k, v in self.__dict__.items()
            if k not in ["event_id", "timestamp", "version", "metadata"]
        }


# Domain Events
@dataclass
class OrderCreated(Event):
    order_id: UUID = field(default_factory=uuid4)
    customer_id: str = ""
    items: List[Dict] = field(default_factory=list)
    total: float = 0.0


@dataclass
class OrderConfirmed(Event):
    order_id: UUID = field(default_factory=uuid4)
    confirmed_at: datetime = field(default_factory=datetime.utcnow)


@dataclass
class OrderShipped(Event):
    order_id: UUID = field(default_factory=uuid4)
    tracking_number: str = ""
    carrier: str = ""


@dataclass
class OrderCancelled(Event):
    order_id: UUID = field(default_factory=uuid4)
    reason: str = ""
    cancelled_by: str = ""


# Aggregate Root
class Aggregate(ABC):
    def __init__(self):
        self._uncommitted_events: List[Event] = []
        self._version: int = 0

    @property
    def uncommitted_events(self) -> List[Event]:
        return self._uncommitted_events

    def clear_uncommitted_events(self) -> None:
        self._uncommitted_events.clear()

    def apply_event(self, event: Event) -> None:
        """Apply event to current state."""
        handler_name = f"_apply_{self._to_snake_case(event.__class__.__name__)}"
        handler = getattr(self, handler_name, None)

        if handler:
            handler(event)

        self._version += 1

    def raise_event(self, event: Event) -> None:
        """Raise new event."""
        event.version = self._version + 1
        self.apply_event(event)
        self._uncommitted_events.append(event)

    @staticmethod
    def _to_snake_case(name: str) -> str:
        import re
        return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()


# Order Aggregate
class Order(Aggregate):
    def __init__(self):
        super().__init__()
        self.id: Optional[UUID] = None
        self.customer_id: Optional[str] = None
        self.items: List[Dict] = []
        self.total: float = 0.0
        self.status: str = "pending"
        self.tracking_number: Optional[str] = None

    @classmethod
    def create(cls, customer_id: str, items: List[Dict]) -> "Order":
        """Factory method to create new order."""
        order = cls()
        total = sum(item["price"] * item["quantity"] for item in items)

        order.raise_event(OrderCreated(
            order_id=uuid4(),
            customer_id=customer_id,
            items=items,
            total=total,
        ))

        return order

    def confirm(self) -> None:
        """Confirm the order."""
        if self.status != "pending":
            raise ValueError(f"Cannot confirm order in {self.status} status")

        self.raise_event(OrderConfirmed(order_id=self.id))

    def ship(self, tracking_number: str, carrier: str) -> None:
        """Ship the order."""
        if self.status != "confirmed":
            raise ValueError(f"Cannot ship order in {self.status} status")

        self.raise_event(OrderShipped(
            order_id=self.id,
            tracking_number=tracking_number,
            carrier=carrier,
        ))

    def cancel(self, reason: str, cancelled_by: str) -> None:
        """Cancel the order."""
        if self.status in ["shipped", "delivered", "cancelled"]:
            raise ValueError(f"Cannot cancel order in {self.status} status")

        self.raise_event(OrderCancelled(
            order_id=self.id,
            reason=reason,
            cancelled_by=cancelled_by,
        ))

    # Event handlers
    def _apply_order_created(self, event: OrderCreated) -> None:
        self.id = event.order_id
        self.customer_id = event.customer_id
        self.items = event.items
        self.total = event.total
        self.status = "pending"

    def _apply_order_confirmed(self, event: OrderConfirmed) -> None:
        self.status = "confirmed"

    def _apply_order_shipped(self, event: OrderShipped) -> None:
        self.status = "shipped"
        self.tracking_number = event.tracking_number

    def _apply_order_cancelled(self, event: OrderCancelled) -> None:
        self.status = "cancelled"


# Event Store
class EventStore(ABC):
    @abstractmethod
    async def append(self, stream_id: str, events: List[Event], expected_version: int) -> None:
        pass

    @abstractmethod
    async def load(self, stream_id: str) -> List[Event]:
        pass


class PostgresEventStore(EventStore):
    """PostgreSQL-based event store."""

    def __init__(self, connection_pool):
        self.pool = connection_pool

    async def append(
        self,
        stream_id: str,
        events: List[Event],
        expected_version: int,
    ) -> None:
        """Append events with optimistic concurrency."""
        async with self.pool.acquire() as conn:
            async with conn.transaction():
                # Check current version
                current_version = await conn.fetchval(
                    """
                    SELECT COALESCE(MAX(version), 0)
                    FROM events WHERE stream_id = $1
                    """,
                    stream_id,
                )

                if current_version != expected_version:
                    raise ConcurrencyError(
                        f"Expected version {expected_version}, "
                        f"but found {current_version}"
                    )

                # Insert events
                for event in events:
                    await conn.execute(
                        """
                        INSERT INTO events (
                            event_id, stream_id, event_type,
                            data, metadata, version, created_at
                        ) VALUES ($1, $2, $3, $4, $5, $6, $7)
                        """,
                        str(event.event_id),
                        stream_id,
                        event.__class__.__name__,
                        json.dumps(event._get_data()),
                        json.dumps(event.metadata),
                        event.version,
                        event.timestamp,
                    )

    async def load(self, stream_id: str) -> List[Event]:
        """Load all events for a stream."""
        async with self.pool.acquire() as conn:
            rows = await conn.fetch(
                """
                SELECT event_type, data, metadata, version, created_at
                FROM events
                WHERE stream_id = $1
                ORDER BY version
                """,
                stream_id,
            )

            return [self._deserialize_event(row) for row in rows]

    def _deserialize_event(self, row) -> Event:
        """Deserialize event from database row."""
        event_class = EVENT_REGISTRY.get(row["event_type"])
        if not event_class:
            raise ValueError(f"Unknown event type: {row['event_type']}")

        data = json.loads(row["data"])
        return event_class(
            version=row["version"],
            timestamp=row["created_at"],
            metadata=json.loads(row["metadata"]),
            **data,
        )


# Repository
class OrderRepository:
    """Repository for Order aggregate."""

    def __init__(self, event_store: EventStore):
        self.event_store = event_store

    async def save(self, order: Order) -> None:
        """Save order by appending events."""
        events = order.uncommitted_events
        if not events:
            return

        stream_id = f"order-{order.id}"
        expected_version = order._version - len(events)

        await self.event_store.append(stream_id, events, expected_version)
        order.clear_uncommitted_events()

    async def load(self, order_id: UUID) -> Optional[Order]:
        """Load order by replaying events."""
        stream_id = f"order-{order_id}"
        events = await self.event_store.load(stream_id)

        if not events:
            return None

        order = Order()
        for event in events:
            order.apply_event(event)

        return order


# CQRS - Read Model Projector
class OrderProjector:
    """Projects events to read model."""

    def __init__(self, read_db):
        self.read_db = read_db

    async def project(self, event: Event) -> None:
        """Project event to read model."""
        handler = getattr(self, f"_handle_{event.__class__.__name__}", None)
        if handler:
            await handler(event)

    async def _handle_OrderCreated(self, event: OrderCreated) -> None:
        await self.read_db.execute(
            """
            INSERT INTO order_summaries (
                order_id, customer_id, total, status, created_at
            ) VALUES ($1, $2, $3, $4, $5)
            """,
            str(event.order_id),
            event.customer_id,
            event.total,
            "pending",
            event.timestamp,
        )

    async def _handle_OrderConfirmed(self, event: OrderConfirmed) -> None:
        await self.read_db.execute(
            """
            UPDATE order_summaries
            SET status = 'confirmed', updated_at = $2
            WHERE order_id = $1
            """,
            str(event.order_id),
            event.confirmed_at,
        )

    async def _handle_OrderShipped(self, event: OrderShipped) -> None:
        await self.read_db.execute(
            """
            UPDATE order_summaries
            SET status = 'shipped',
                tracking_number = $2,
                carrier = $3,
                updated_at = NOW()
            WHERE order_id = $1
            """,
            str(event.order_id),
            event.tracking_number,
            event.carrier,
        )


# Saga Pattern for Distributed Transactions
class OrderSaga:
    """Saga for order processing across services."""

    def __init__(self, event_bus, order_service, payment_service, inventory_service):
        self.event_bus = event_bus
        self.order_service = order_service
        self.payment_service = payment_service
        self.inventory_service = inventory_service

    async def handle_order_created(self, event: OrderCreated) -> None:
        """Start saga when order is created."""
        try:
            # Step 1: Reserve inventory
            await self.inventory_service.reserve(event.order_id, event.items)

            # Step 2: Process payment
            await self.payment_service.charge(
                event.order_id,
                event.customer_id,
                event.total,
            )

            # Step 3: Confirm order
            await self.order_service.confirm(event.order_id)

        except InventoryError:
            # Compensate: Cancel order
            await self.order_service.cancel(
                event.order_id,
                reason="Insufficient inventory",
            )

        except PaymentError:
            # Compensate: Release inventory
            await self.inventory_service.release(event.order_id)
            await self.order_service.cancel(
                event.order_id,
                reason="Payment failed",
            )

    async def handle_order_cancelled(self, event: OrderCancelled) -> None:
        """Compensate when order is cancelled."""
        # Release inventory
        await self.inventory_service.release(event.order_id)

        # Refund payment if charged
        await self.payment_service.refund(event.order_id)
```

```yaml
# Event Schema Registry (AsyncAPI)
asyncapi: "2.6.0"
info:
  title: Order Events API
  version: "1.0.0"
  description: Event-driven order processing system

channels:
  orders/created:
    publish:
      message:
        $ref: "#/components/messages/OrderCreated"

  orders/confirmed:
    publish:
      message:
        $ref: "#/components/messages/OrderConfirmed"

  orders/shipped:
    publish:
      message:
        $ref: "#/components/messages/OrderShipped"

components:
  messages:
    OrderCreated:
      payload:
        type: object
        properties:
          event_id:
            type: string
            format: uuid
          order_id:
            type: string
            format: uuid
          customer_id:
            type: string
          items:
            type: array
            items:
              type: object
              properties:
                product_id:
                  type: string
                quantity:
                  type: integer
                price:
                  type: number
          total:
            type: number
          timestamp:
            type: string
            format: date-time
```

## Best Practices

### Event Design

- Events are immutable facts
- Use past tense naming (OrderCreated)
- Include all relevant data
- Version your events

### Event Sourcing

- Keep events small and focused
- Use snapshots for long streams
- Handle schema evolution
- Implement idempotency

### CQRS

- Separate read and write models
- Optimize read models for queries
- Accept eventual consistency
- Use projections for views

### Reliability

- Implement outbox pattern
- Use idempotent consumers
- Handle duplicate events
- Monitor event processing lag

Event-driven architecture powers **Netflix, Uber, and LinkedIn** at massive scale.

You design scalable, resilient event-driven systems with proper patterns and practices.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.