Skip to main content

📅 Events

Event Sourcing is all about saving changes in your application state. Such changes are represented by events, and needless to say, they are quite important 🙃

Events that concern the same entity (like a Pokemon) are aggregated through a common id called aggregateId (and vice versa, events that have the same aggregateId represent changes of the same entity). The index of an event in such a serie of events is called its version.

Events

In Castore, stored events (also called event details) always have exactly the following properties:

  • aggregateId (string)
  • version (integer ≥ 1)
  • timestamp (string): A date in ISO 8601 format
  • type (string): A string identifying the business meaning of the event
  • payload (?any = never): A payload of any type
  • metadata (?any = never): Some metadata of any type
import type { EventDetail } from '@castore/core';

type PokemonAppearedEventDetail = EventDetail<
'POKEMON_APPEARED',
{ name: string; level: number },
{ trigger?: 'random' | 'scripted' }
>;

// 👇 Equivalent to:
type PokemonAppearedEventDetail = {
aggregateId: string;
version: number;
timestamp: string;
type: 'POKEMON_APPEARED';
payload: { name: string; level: number };
metadata: { trigger?: 'random' | 'scripted' };
};

Events are generally classified in events types (not to confuse with TS types). Castore lets you declare them via the EventType class:

import { EventType } from '@castore/core';

const pokemonAppearedEventType = new EventType<
'POKEMON_APPEARED',
{ name: string; level: number },
{ trigger?: 'random' | 'scripted' }
>({ type: 'POKEMON_APPEARED' });
info

Note that we only provided TS types for payload and metadata properties. That is because, as stated in the core design, Castore is meant to be as flexible as possible, and that includes the validation library you want to use (if any): The EventType class can be used directly if no validation is required, or implemented by other classes which will add run-time validation methods to it 👍

🔧 Reference

Constructor:

  • type (string): The event type
import { EventType } from '@castore/core';

const pokemonAppearedEventType = new EventType({
type: 'POKEMON_APPEARED',
});

Properties:

  • type (string): The event type
const eventType = pokemonAppearedEventType.type;
// => 'POKEMON_APPEARED'

Type Helpers:

  • EventTypeDetail: Returns the event detail TS type of an EventType
import type { EventTypeDetail } from '@castore/core';

type PokemonAppearedEventTypeDetail = EventTypeDetail<
typeof pokemonAppearedEventType
>;

// 👇 Equivalent to:
type PokemonCaughtEventTypeDetail = {
aggregateId: string;
version: number;
timestamp: string;
type: 'POKEMON_APPEARED';
payload: { name: string; level: number };
metadata: { trigger?: 'random' | 'scripted' };
};
  • EventTypeDetails: Returns the events details of a list of EventType
import type { EventTypeDetails } from '@castore/core';

type PokemonEventTypeDetails = EventTypeDetails<
[typeof pokemonAppearedEventType, typeof pokemonCaughtEventType]
>;
// => EventTypeDetail<typeof pokemonAppearedEventType>
// | EventTypeDetail<typeof pokemonCaughtEventType>