# Event-Driven Architecture

## Envelope

All events use `shared.events.EventEnvelope`:

```json
{
  "event_id": "uuid",
  "event_type": "tenant.created",
  "aggregate_type": "tenant",
  "aggregate_id": "uuid",
  "tenant_id": "uuid|null",
  "source_service": "core-service",
  "payload": {},
  "occurred_at": "ISO-8601"
}
```

## Outbox → Inbox ([ADR-006](adr/ADR-006.md))

1. Producer writes business row + `outbox_events` in one transaction.
2. Worker (`process_outbox_events`) publishes pending rows.
3. Consumer inserts `inbox_events` keyed by `event_id` (idempotency) then handles payload.

## Naming

`{aggregate}.{past_tense_verb}` — e.g. `tenant.created`, `subscription.updated`, `user.registered`.

## Catalog

Canonical list → [event-catalog.md](../reference/event-catalog.md)

## Future

Replace in-process/Celery-only publish with a real message bus without changing envelope or outbox ownership.

## Related Documents

- [Services Contracts](../reference/services-contracts.md)
- [Service Architecture](service-architecture.md)
- [ADR-006](adr/ADR-006.md)
