Skip to content

Storing Events and Preconditions

Writing to the event store is a single, atomic operation: you hand EventSourcingKit one or more event candidates and, optionally, a set of preconditions that must hold for the write to be accepted. If any precondition fails, nothing is written.

Event Candidates and Subjects

You never store a bare event. Instead, you wrap it in an EventCandidate, which pairs the event data with a Subject:

var subject = new Subject($"/books/{bookId}");
var candidate = new EventCandidate(
    subject,
    new BookRegistered(bookId, title, author)
);

A subject is a hierarchical path that identifies the entity an event belongs to, and it always starts with a slash, for example /books/42. You do not supply the event's type name or source: EventSourcingKit derives the type from the [EventType] attribute and the source from your configuration.

If you configure a SubjectPrefix, it is prepended to every subject automatically, on both writing and reading, which lets several applications share one store without colliding.

Storing One or Many Events

Use StoreEvent for a single event and StoreEvents for several. Storing several at once is atomic: they are written together or not at all.

await eventStore.StoreEvents(
    [firstCandidate, secondCandidate],
    [new IsSubjectPristinePrecondition(subject)],
    cancellationToken
);

Both methods return the stored events, now carrying their assigned Id, Time, and other metadata.

Preconditions

Preconditions are how you enforce consistency at the moment of writing. EventSourcingKit offers four:

  • IsSubjectPristinePrecondition – the write succeeds only if no event has ever been stored under the subject. Use it to create an entity exactly once.
  • IsSubjectPopulatedPrecondition – the write succeeds only if the subject already has events. Use it when an entity must already exist.
  • IsSubjectOnEventIdPrecondition – the write succeeds only if the subject's latest event has the given Id. This is optimistic concurrency: you read state up to a known event, then write on the condition that nothing has changed since.
  • IsEventQlQueryTruePrecondition – the write succeeds only if the given EventQL query evaluates to true, for arbitrary, query-based invariants.

Because preconditions are checked by the store itself, they hold even when several writers act at the same time.

For More Information