Event
Events are the centrepiece of event-sourced systems. They represent both critical points of the business process but are also used as the state. That enables you to reflect your business into the code better, getting the synergy. Let's model a simple business process: a shopping cart. You can open it, add or remove the product from it and confirm or cancel.
Event type helps to keep the event definition aligned. It's not a must, but it helps to ensure that it has a type name defined (e.g. ProductItemAddedToShoppingCart
) and read-only payload data.
Usage
You can use it as follows
ts
import type { Event } from '@event-driven-io/emmett';
type ProductItemAddedToShoppingCart = Event<
'ProductItemAddedToShoppingCart',
{
shoppingCartId: string;
productItem: PricedProductItem;
}
>;
The type is a simple wrapper to ensure the structure's correctness. It defines:
- type - event type name,
- data - represents the business data the event contains. It has to be a record structure; primitives are not allowed,
- metadata - represents the generic data event contains. It can represent telemetry, user id, tenant id, timestamps and other information that can be useful for running infrastructure. It has to be a record structure; primitives are not allowed.
See also
Find more context in getting started guide
ts
import type { DefaultRecord } from './';
import type {
AnyRecordedMessageMetadata,
CombinedMessageMetadata,
CommonRecordedMessageMetadata,
GlobalPositionTypeOfRecordedMessageMetadata,
RecordedMessage,
RecordedMessageMetadata,
RecordedMessageMetadataWithGlobalPosition,
RecordedMessageMetadataWithoutGlobalPosition,
StreamPositionTypeOfRecordedMessageMetadata,
} from './message';
export type BigIntStreamPosition = bigint;
export type BigIntGlobalPosition = bigint;
export type Event<
EventType extends string = string,
EventData extends DefaultRecord = DefaultRecord,
EventMetaData extends DefaultRecord | undefined = undefined,
> = Readonly<
EventMetaData extends undefined
? {
type: EventType;
data: EventData;
}
: {
type: EventType;
data: EventData;
metadata: EventMetaData;
}
> & { readonly kind?: 'Event' };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AnyEvent = Event<any, any, any>;
export type EventTypeOf<T extends Event> = T['type'];
export type EventDataOf<T extends Event> = T['data'];
export type EventMetaDataOf<T extends Event> = T extends { metadata: infer M }
? M
: undefined;
export type CreateEventType<
EventType extends string,
EventData extends DefaultRecord,
EventMetaData extends DefaultRecord | undefined = undefined,
> = Readonly<
EventMetaData extends undefined
? {
type: EventType;
data: EventData;
}
: {
type: EventType;
data: EventData;
metadata: EventMetaData;
}
> & { readonly kind?: 'Event' };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const event = <EventType extends Event<string, any, any>>(
...args: EventMetaDataOf<EventType> extends undefined
? [type: EventTypeOf<EventType>, data: EventDataOf<EventType>]
: [
type: EventTypeOf<EventType>,
data: EventDataOf<EventType>,
metadata: EventMetaDataOf<EventType>,
]
): EventType => {
const [type, data, metadata] = args;
return metadata !== undefined
? ({ type, data, metadata, kind: 'Event' } as EventType)
: ({ type, data, kind: 'Event' } as EventType);
};
export type CombinedReadEventMetadata<
EventType extends Event = Event,
EventMetaDataType extends
AnyRecordedMessageMetadata = AnyRecordedMessageMetadata,
> = CombinedMessageMetadata<EventType, EventMetaDataType>;
export type ReadEvent<
EventType extends Event = Event,
EventMetaDataType extends
AnyRecordedMessageMetadata = AnyRecordedMessageMetadata,
> = RecordedMessage<EventType, EventMetaDataType>;
export type AnyReadEvent<
EventMetaDataType extends AnyReadEventMetadata = AnyReadEventMetadata,
> = ReadEvent<AnyEvent, EventMetaDataType>;
export type CommonReadEventMetadata<StreamPosition = BigIntStreamPosition> =
CommonRecordedMessageMetadata<StreamPosition>;
export type ReadEventMetadata<
GlobalPosition = undefined,
StreamPosition = BigIntStreamPosition,
> = RecordedMessageMetadata<GlobalPosition, StreamPosition>;
export type AnyReadEventMetadata = AnyRecordedMessageMetadata;
export type ReadEventMetadataWithGlobalPosition<
GlobalPosition = BigIntGlobalPosition,
> = RecordedMessageMetadataWithGlobalPosition<GlobalPosition>;
export type ReadEventMetadataWithoutGlobalPosition<
StreamPosition = BigIntStreamPosition,
> = RecordedMessageMetadataWithoutGlobalPosition<StreamPosition>;
export type GlobalPositionTypeOfReadEventMetadata<ReadEventMetadataType> =
GlobalPositionTypeOfRecordedMessageMetadata<ReadEventMetadataType>;
export type StreamPositionTypeOfReadEventMetadata<ReadEventMetadataType> =
StreamPositionTypeOfRecordedMessageMetadata<ReadEventMetadataType>;