# Event Structure and Envelopes — Event-Driven Architecture & CQRS

Source: https://www.skillbyai.com/en/event-driven-architecture/e-design

> Design events with clear names, identifiers and metadata.

## What every event should carry

A well-formed event has two parts. The **envelope** (metadata) answers *what, when, where from and how to trace it*: a unique **event ID** (for deduplication), the **event type** and **schema version**, the **source**, an **occurrence time**, the **subject** or aggregate ID (for ordering and partitioning) and **correlation/causation IDs** to link events into a flow. The **payload** holds business data. The CNCF **CloudEvents** specification standardises envelope attributes (`id`, `source`, `type`, `specversion`, `time`, `subject`, `datacontenttype`) so tools and brokers can route events consistently. Name types in the past tense, scoped by domain (`com.shop.orders.OrderPlaced` or `orders.order.placed`). Use explicit money and time formats: amounts as strings or minor units with a currency, timestamps in UTC ISO 8601.

## Envelope and payload

Metadata on the outside for routing and tracing, business data on the inside.

![An envelope shape with small label strips along its edge and a document sheet partly sticking out of it.](assets/figures/event-driven-architecture/section-2-map.svg) — Figure 2.1 — An event envelope wrapping a business payload.

## A CloudEvents-style event

The envelope fields let any consumer deduplicate, route and trace the event.

```json
{
  "specversion": "1.0",
  "id": "6f1c2a8e-3b4d-4c55-9a1e-0d2f7b9c1e11",
  "source": "/services/orders",
  "type": "com.shop.orders.OrderPlaced.v1",
  "subject": "order/o-1001",
  "time": "2026-10-03T06:45:12Z",
  "datacontenttype": "application/json",
  "correlationid": "chk-77a1",
  "data": {
    "orderId": "o-1001",
    "customerId": "c-42",
    "currency": "INR",
    "totalMinor": 149900,
    "items": [{ "sku": "notebook", "qty": 3, "unitPriceMinor": 49966 }]
  }
}
```

## Never reuse an event ID

Consumers use the event ID to detect duplicates. If a retry generates a new ID for the same fact, deduplication silently fails. Create the ID once, when the fact is recorded, and keep it through every retry.

**Quiz:** Why does an event carry a unique ID in its envelope?

- [ ] To make the JSON larger
- [ ] To choose the broker
- [x] So consumers can detect and ignore duplicate deliveries
- [ ] To encrypt the payload

*Answer:* So consumers can detect and ignore duplicate deliveries. At-least-once delivery means duplicates happen; a stable ID lets consumers deduplicate.
