# Event-Driven Architecture — Part 1: Events as the Integration Contract

The previous topics covered the plumbing — brokers, queues, topics, delivery guarantees. **Event-driven architecture (EDA)** is the design style built on top: services communicate primarily by **publishing facts about what happened**, and other services **react** to those facts, instead of calling each other directly.

## 1. Request-Driven vs Event-Driven

```
request-driven (orchestrated by the caller):
  orders ──HTTP──► payments
         ──HTTP──► inventory
         ──HTTP──► email
         ──HTTP──► loyalty            orders must know (and wait for) every downstream service

event-driven (choreographed by reactions):
  orders ──publish OrderPlaced──► broker ──► payments   (charge)
                                         ──► inventory  (reserve)
                                         ──► email      (confirm)
                                         ──► loyalty    (add points)
  orders knows nothing about who listens
```

| | request-driven | event-driven |
|---|---|---|
| coupling | caller knows callees, their APIs and availability | publisher knows only the event schema |
| adding a consumer | change the caller | subscribe; no change to the publisher |
| failure of a downstream | caller fails or must handle it | event waits in the broker; consumer catches up |
| latency to the end user | sum of the chain | publisher returns immediately |
| consistency | can be immediate | **eventual** |
| understanding the flow | read the caller | trace events across services (harder) |

## 2. Three Kinds of Events

| Kind | Carries | Example | Trade-off |
|---|---|---|---|
| **Event notification** | "something happened" + IDs | `OrderPlaced { orderId }` | tiny, but consumers call back for details (coupling + load) |
| **Event-carried state transfer** | the data consumers need | `OrderPlaced { orderId, customerId, items, total }` | consumers keep a local copy, no callbacks; larger events, data duplication |
| **Domain event (for event sourcing)** | the state change itself, as the source of truth | `ItemAddedToCart { cartId, sku, qty }` | full history and replay; see Part 2 |

Most systems use event-carried state transfer for integration events: consumers stay autonomous even when the publisher is down.

## 3. Designing Good Events

- **Past tense, business language**: `OrderPlaced`, `PaymentFailed` — not `CreateOrder` (that's a command, `05-queues_and_topics/` §2) and not `OrderTableRowUpdated` (that leaks the schema).
- **Envelope + payload**:
  ```json
  {
    "eventId": "01HZX6K8…",            // unique, for dedup (09-delivery_guarantees/)
    "eventType": "OrderPlaced",
    "schemaVersion": 2,
    "occurredAt": "2025-06-12T10:15:03Z",
    "source": "orders-service",
    "correlationId": "req-81f…",        // ties to the original request / trace
    "data": { "orderId": "o-17", "customerId": "c-4", "total": 4599, "currency": "EUR" }
  }
  ```
  (CloudEvents is a standard for this envelope.)
- **Key by the aggregate id** (`orderId`) so all events of one order stay ordered (`08-…/`).
- **Schema evolution**: only make **backward-compatible** changes (add optional fields; never rename/remove/change types) within a version; use a schema registry (Avro/Protobuf/JSON Schema) to enforce it; publish a new event type or version for breaking changes.

## 4. Choreography vs Orchestration

Multi-step business processes (place order → pay → reserve → ship) can be coordinated two ways:

- **Choreography**: each service reacts to events and emits its own (`OrderPlaced` → payments emits `PaymentSucceeded` → inventory emits `StockReserved` → shipping…). No central brain. Great for simple flows; for long flows the process is implicit and hard to see or change.
- **Orchestration**: a **process manager / saga orchestrator** sends commands and listens for replies (`distributed-systems/fundamentals/08-distributed_transactions`). The flow is explicit in one place; that service is a coupling point.

Rule of thumb: choreograph *notifications and loosely related reactions*; orchestrate *a business transaction with compensations*.

## 5. Eventual Consistency, Made Visible

After `OrderPlaced`, the loyalty service's points balance is updated "soon" — not in the same transaction. Design for it:

- UIs show pending states ("Points will appear shortly").
- Read-your-writes where it matters: the service that accepted the write serves the immediate follow-up read.
- Consumers must be idempotent and tolerant of reordering (`08-…/`, `09-…/`).

## Common Pitfalls (Part 1)

1. **Commands disguised as events** (`SendEmailRequested` published to "whoever") — hidden coupling.
2. **Leaking internal schemas** (`orders_table_updated`) — every DB refactor breaks consumers.
3. **Notification events that trigger N callbacks** to the publisher — a self-inflicted DDoS on every event.
4. **Breaking schema changes** without versioning.
5. **Publishing outside the DB transaction** — lost events (use the outbox, Part 2).

## Quick Reference (Part 1)

```
event = past-tense fact, envelope {eventId, type, version, occurredAt, correlationId} + data
key by aggregate id • event-carried state transfer for integration • backward-compatible evolution
choreography for reactions • orchestration for multi-step transactions with compensation
```
