Integration events
An integration event is what other bounded contexts receive when something happens in yours: plain JSON in your published language, with an explicit type, a schema version and the operation it belongs to. The domain event stays internal; an event translator builds the integration event.
type OrderPlacedRepresentation = PublishedLanguage<IntegrationEvent<"OrderPlaced", { orderId: string; total: number }>>;When to use
Declare one integration event for every domain event that leaves the bounded context. Because it is JSON, the outbox stores it and reads it back as is, a broker carries it, and the consumer reads it without sharing a single class with you. Renaming a domain event or one of its fields changes nothing for the other contexts.
Usage
Declare the events you publish
Declare them in the published language of the bounded context. type is a string you choose, not a class name; payload holds only JSON.
import type { IntegrationEvent, PublishedLanguage } from "@alveolus/core";
export type OrderPlacedRepresentation = PublishedLanguage<IntegrationEvent<"OrderPlaced", { orderId: string; total: number; currency: string }>>;Evolve an event
Raise version when the payload changes in a way consumers must know about, and keep publishing the old version while they move.
export type OrderPlacedV2Representation = PublishedLanguage<
IntegrationEvent<"OrderPlaced", { orderId: string; total: { amount: number; currency: string } }>
>;Consume an event from another context
The consumer redeclares, in its own published language, the fields it reads; it never imports the types of the emitter. It deduplicates by id: delivery is at least once.
export type OrderPlacedRepresentation = PublishedLanguage<IntegrationEvent<"OrderPlaced", { orderId: string; total: number }>>;Checked by bc-isolation, which forbids importing another context's published language.
Reference
type IntegrationEvent<Type extends string = string, Payload extends JsonValue = JsonValue> = PublishedLanguage<{
readonly id: string;
readonly type: Type;
readonly version: number;
readonly source: string;
readonly occurredAt: string;
readonly correlationId: string;
readonly causationId?: string;
readonly payload: Payload;
}>;
interface IntegrationEventContext {
readonly correlationId: string;
readonly causationId?: string;
}| Type parameter | Description |
|---|---|
Type | The name of the event in the contract. |
Payload | The JSON data of the event. |
| Member | Type | Description |
|---|---|---|
id | string | The id of the domain event, used to deduplicate. |
type | Type | The name of the event in the contract. |
version | number | The version of the payload schema. |
source | string | The bounded context that emitted the event. |
occurredAt | string | When the domain event happened, as an ISO 8601 string. |
correlationId | string | The operation the event belongs to, across contexts. |
causationId | string, optional | The message that caused this event. |
payload | Payload | The data of the event. |
Caveats
IntegrationEventis a type: nothing exists at runtime. Build values with an event translator.- One integration event per domain event: both share the same
id. AnyIntegrationEventis any integration event, used by the outbox and the publisher.IntegrationEventContextis what a command handler passes to the translator.
Import from @alveolus/core or @alveolus/core/integration-events.
See also
- Event translators, which build integration events
- Outbox and Event publishers, which store and send them
- Published Language