Getting events
Event sequences support multiple ways of reading events depending on your needs. The IEventSequence APIs (and the specialized IEventLog) cover common patterns such as:
- Reading events from a sequence in order
- Reading events for a specific event source
- Reading a range of events based on sequence numbers
- Reading a fixed number of events from the tail
Use the client APIs to select the pattern that best matches your scenario, such as replaying state, building read models, or auditing changes.
Examples
Section titled “Examples”Read events for a single event source
Section titled “Read events for a single event source”using Cratis.Chronicle.Events;using Cratis.Chronicle.EventSequences;using System.Collections.Immutable;
[EventType]public record GettingEventsOrderPlaced(string OrderId, decimal Total);
[EventType]public record GettingEventsOrderCancelled(string OrderId, string Reason);
public class GettingEventsOrderHistoryReader(IEventLog eventLog){ public async Task<IImmutableList<AppendedEvent>> GetOrderEvents(EventSourceId orderId) { // Filters the timeline to only the order events you care about. var eventTypes = new[] { typeof(GettingEventsOrderPlaced).GetEventType(), typeof(GettingEventsOrderCancelled).GetEventType() };
return await eventLog.GetForEventSourceIdAndEventTypes(orderId, eventTypes); }}import io.cratis.chronicle.IEventStoreimport io.cratis.chronicle.events.EventType
@EventTypedata class EventsForSourceAccountOpened(val accountId: String = "", val ownerName: String = "")
suspend fun getAccountOpenedEvents(store: IEventStore, accountId: String) = store.eventLog.getForEventSourceIdAndEventTypes(accountId, listOf(EventsForSourceAccountOpened::class))import io.cratis.chronicle.EventStore;import io.cratis.chronicle.events.EventType;import io.cratis.chronicle.eventSequences.AppendedEvent;
import java.util.List;
import io.cratis.chronicle.java.EventLogJavaBridge;
@EventTyperecord EventsForSourceAccountOpened(String accountId, String ownerName) {}
class EventsGettingEventsForEventSource { List<AppendedEvent> getAccountOpenedEvents(EventStore store, String accountId) { return EventLogJavaBridge.getForEventSourceIdAndEventTypes( store.getEventLog(), accountId, List.of(EventsForSourceAccountOpened.class)); }}defmodule MyApp.Events.GettingEventsOrderPlaced do use Chronicle.Events.EventType, id: "getting-events-order-placed"
defstruct [:order_id, :total]end
defmodule MyApp.Events.GettingEventsOrderCancelled do use Chronicle.Events.EventType, id: "getting-events-order-cancelled"
defstruct [:order_id, :reason]end
defmodule MyApp.GettingEventsOrderHistoryReader do alias Chronicle.EventSequences.EventLog alias MyApp.Events.{GettingEventsOrderCancelled, GettingEventsOrderPlaced}
def get_order_events(order_id) do # Filters the timeline to only the order events you care about. EventLog.get_for_event_source(order_id, event_types: [GettingEventsOrderPlaced, GettingEventsOrderCancelled] ) endendimport { eventType, IEventStore } from '@cratis/chronicle';
@eventType()class GettingEventsOrderPlaced { constructor(readonly orderId: string = '', readonly total: number = 0) {}}
@eventType()class GettingEventsOrderCancelled { constructor(readonly orderId: string = '', readonly reason: string = '') {}}
async function getOrderEvents(store: IEventStore, orderId: string): Promise<void> { // Filters the timeline to only the order events you care about. const events = await store.eventLog.getForEventSourceIdAndEventTypes( orderId, [GettingEventsOrderPlaced, GettingEventsOrderCancelled]);
for (const event of events) { console.log(`${event.eventType.id.value} at sequence ${event.context.sequenceNumber}`); }}Kotlin, Java, and TypeScript don’t currently expose a way to read raw events back for an event source — their public event sequence APIs only support appending and checking for existence.
Read from a checkpoint
Section titled “Read from a checkpoint”using Cratis.Chronicle.Events;using Cratis.Chronicle.EventSequences;using System.Collections.Immutable;
public class GettingEventsReplayEvents(IEventLog eventLog){ public async Task<IImmutableList<AppendedEvent>> ReadFrom(EventSequenceNumber sequenceNumber) { // Replays from a known checkpoint to rebuild projections or read models. return await eventLog.GetFromSequenceNumber(sequenceNumber); }}import { EventSequenceNumber, IEventStore } from '@cratis/chronicle';
async function readFrom(store: IEventStore, sequenceNumber: EventSequenceNumber): Promise<void> { // Replays from a known checkpoint to rebuild projections or read models. const events = await store.eventLog.getFromSequenceNumber(sequenceNumber);
for (const event of events) { console.log(`${event.eventType.id.value} at sequence ${event.context.sequenceNumber}`); }}This is currently a C#-only capability — no other client exposes a way to resume reading from an arbitrary sequence number.
Read the last events in a sequence
Section titled “Read the last events in a sequence”using Cratis.Chronicle.Events;using Cratis.Chronicle.EventSequences;using System.Collections.Immutable;using System.Linq;
public class GettingEventsTailReader(IEventLog eventLog){ public async Task<IImmutableList<AppendedEvent>> ReadLast(int count) { // Reads from the computed start and trims in memory to the requested count. var tail = await eventLog.GetTailSequenceNumber(); var start = tail.IsActualValue && tail.Value >= (ulong)count ? tail - (count - 1) : EventSequenceNumber.First;
var events = await eventLog.GetFromSequenceNumber(start); return events.TakeLast(count).ToImmutableList(); }}import { IEventStore } from '@cratis/chronicle';
async function reportSequencePosition(store: IEventStore): Promise<void> { // The tail is the most recently appended event; unset (EventSequenceNumber.unset) // when the sequence is empty. const tail = await store.eventLog.getTailSequenceNumber();
// getNextSequenceNumber is one past the tail, or EventSequenceNumber.first when // empty - the sequence number the next appended event will receive. const next = await store.eventLog.getNextSequenceNumber();
console.log(`Tail: ${tail.value}, next append will be at: ${next.value}`);}Reading the tail this way also relies on resuming from a sequence number, so it’s currently C#-only too. Kotlin, Java, and TypeScript can still get the tail sequence number (see Getting state), just not the events at it.