3.4 KiB
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
DomainEventschema —{id, type, aggregateId, aggregateType, payload, occurredAt}fully typedtypeas a dot-separated event-type name (e.g."user.registered") — convention over constraintaggregateId+aggregateTypetogether identify which aggregate instance raised the eventpayloadtyped asz.unknown()— the envelope is payload-agnostic; aggregate event types carry their own Zod schemaoccurredAtas 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
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
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<typeof DomainEvent>
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.