# domain-event ## Purpose Defines the `DomainEvent` Zod schema — the canonical wire format for immutable domain events flowing through the Suaveplan event-sourcing and event-driven integration infrastructure. Domain events record facts about things that happened within a bounded context: "UserRegistered", "OrderPlaced", "PaymentFailed". This schema provides the structural envelope; the domain-specific payload schema is defined and validated by each aggregate separately. ## Features - `DomainEvent` schema — `{id, type, aggregateId, aggregateType, payload, occurredAt}` fully typed - `type` as a dot-separated event-type name (e.g. `"user.registered"`) — convention over constraint - `aggregateId` + `aggregateType` together identify which aggregate instance raised the event - `payload` typed as `z.unknown()` — the envelope is payload-agnostic; aggregate event types carry their own Zod schema - `occurredAt` as an ISO 8601 string with timezone offset — the moment in domain time when the event occurred - Full TypeScript inference via `z.infer<>` — no manual interface duplication - Zod v4 compatible ## Usage: Basic Example ```ts import { DomainEvent } from "@suaveplan/api-schemas/events"; const event = DomainEvent.parse({ id: "evt_01J8ZGCZ7ECTFAKEUID123456", type: "user.registered", aggregateId: "user_abc123", aggregateType: "User", payload: { email: "alice@example.com", name: "Alice" }, occurredAt: "2024-06-01T12:00:00Z", }); ``` ## Usage: Advanced Example ```ts import { DomainEvent } from "@suaveplan/api-schemas/events"; import z from "zod"; // Narrow the payload for a specific event type const UserRegisteredPayload = z.object({ email: z.string().email(), name: z.string(), }); function handleUserRegistered(raw: unknown) { const event = DomainEvent.parse(raw); if (event.type !== "user.registered") return; const payload = UserRegisteredPayload.parse(event.payload); console.log(`New user: ${payload.name} (${payload.email})`); } ``` ## API Reference ### `DomainEvent` Zod object schema — immutable domain event record. | Field | Type | Description | |-----------------|---------------------------------|------------------------------------------------------| | `id` | `string` (min 1) | Unique event identifier | | `type` | `string` (min 1) | Event type name (e.g. `"user.registered"`) | | `aggregateId` | `string` (min 1) | Identifier of the source aggregate | | `aggregateType` | `string` (min 1) | Type name of the source aggregate | | `payload` | `unknown` | Domain-specific event data | | `occurredAt` | `string` (ISO 8601 with offset) | Domain time when the event occurred | **Type export:** `type DomainEvent = z.infer` ## Implementation Notes `payload` is `z.unknown()` intentionally — the base schema validates the event envelope, not the payload contents. Each aggregate type defines its own Zod schema for each of its event types; payload validation is performed after routing by event type. This pattern keeps the event bus loosely coupled and avoids a centralised schema registry for domain events, which would become a bottleneck in a large bounded-context landscape.