Table of Contents

Interface IEventStore

Namespace
Opossum
Assembly
Opossum.dll
public interface IEventStore
Extension Methods

Methods

AppendAsync(NewEvent[], AppendCondition?, CancellationToken)

Atomically appends one or more events to the event store, optionally enforcing a concurrency guard.

Task AppendAsync(NewEvent[] events, AppendCondition? condition, CancellationToken cancellationToken = default)

Parameters

events NewEvent[]

One or more events to append. The array must not be empty and every Event must have a non-empty EventType.

condition AppendCondition

Optional AppendCondition that guards the append against concurrent writes. Pass null for an unconditional append.

cancellationToken CancellationToken

Token to cancel the operation. When cancelled, the semaphore wait is aborted and no events are written.

Returns

Task

Exceptions

AppendConditionFailedException

Thrown when condition is set and a conflicting event was appended between the caller's read and this append. Catch this exception and retry the full read → decide → append cycle.

ArgumentNullException

When events is null.

ArgumentException

When events is empty or contains an event with a null/empty EventType.

TimeoutException

Thrown when the cross-process append lock cannot be acquired within CrossProcessLockTimeout. This can occur when multiple application instances share the same store directory and one instance holds the lock for an unusually long time. Increase CrossProcessLockTimeout if this occurs regularly, or investigate the process that is holding the lock.

ReadAsync(Query, ReadOption[]?, long?, int?)

Reads events matching query from the event store.

Task<SequencedEvent[]> ReadAsync(Query query, ReadOption[]? readOptions, long? fromPosition = null, int? maxCount = null)

Parameters

query Query

Filter — use All() to read every event.

readOptions ReadOption[]

Optional read options (e.g. Descending).

fromPosition long?

When provided, only events with Position > fromPosition are returned. Pass the highest position already processed to resume reading from a known checkpoint. When null (the default) all matching events are returned.

maxCount int?

When provided, at most this many events are returned. Combine with fromPosition to page through large result sets without loading all events into memory at once. When null (the default) all matching events are returned.

Returns

Task<SequencedEvent[]>

ReadLastAsync(Query, CancellationToken)

Returns the event with the highest sequence position that matches query, or null when the store contains no matching events.

Task<SequencedEvent?> ReadLastAsync(Query query, CancellationToken cancellationToken = default)

Parameters

query Query

Filter — use All() to find the globally last event.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<SequencedEvent>

The matching event with the highest sequence position, or null when no events in the store match query.

Remarks

This is the efficient read step for DCB patterns that require knowledge of only the most recent event of a given type — for example, reading the last InvoiceCreatedEvent to determine the next invoice number in an unbroken monotonic sequence.

Typical DCB pattern for consecutive sequences:

// Step 1 — Read: find the last invoice event
var query = Query.FromEventTypes(nameof(InvoiceCreatedEvent));
var last = await eventStore.ReadLastAsync(query);

// Step 2 — Decide: derive the next number var nextNumber = last is null ? 1 : ((InvoiceCreatedEvent)last.Event.Event).InvoiceNumber + 1;

// Step 3 — Append with a guard that fails if any new invoice appeared in the meantime var condition = new AppendCondition { FailIfEventsMatch = query, AfterSequencePosition = last?.Position // null → reject if ANY invoice already exists }; await eventStore.AppendAsync([new NewEvent { Event = new InvoiceCreatedEvent(nextNumber) }], condition); // Throws AppendConditionFailedException on conflict — retry the full cycle.

Only one event file is read from storage regardless of how many total events match the query, making this significantly more efficient than ReadAsync(..., [ReadOption.Descending])[0] for large event streams.

Exceptions

ArgumentNullException

When query is null.