Initial Commit
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# 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<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.
|
||||
Reference in New Issue
Block a user