Skip to content

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.

ts
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.

src/ordering/published-language/order-placed.representation.ts
ts
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.

src/ordering/published-language/order-placed-v2.representation.ts
ts
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.

src/billing/published-language/order-placed.representation.ts
ts
export type OrderPlacedRepresentation = PublishedLanguage<IntegrationEvent<"OrderPlaced", { orderId: string; total: number }>>;

Checked by bc-isolation, which forbids importing another context's published language.

Reference ​

ts
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 parameterDescription
TypeThe name of the event in the contract.
PayloadThe JSON data of the event.
MemberTypeDescription
idstringThe id of the domain event, used to deduplicate.
typeTypeThe name of the event in the contract.
versionnumberThe version of the payload schema.
sourcestringThe bounded context that emitted the event.
occurredAtstringWhen the domain event happened, as an ISO 8601 string.
correlationIdstringThe operation the event belongs to, across contexts.
causationIdstring, optionalThe message that caused this event.
payloadPayloadThe data of the event.

Caveats

  • IntegrationEvent is a type: nothing exists at runtime. Build values with an event translator.
  • One integration event per domain event: both share the same id.
  • AnyIntegrationEvent is any integration event, used by the outbox and the publisher. IntegrationEventContext is what a command handler passes to the translator.

Import from @alveolus/core or @alveolus/core/integration-events.

See also ​

Released under the MIT License.