Skip to content

Preconditions

This guide explains how to use preconditions in EventSourcingDB to ensure that events are only written when certain expectations are met. Preconditions allow you to express business or consistency constraints declarativelydirectly as part of a write request.

Why Preconditions Matter

In many systems, it's not enough to simply write an event. You often want to do so only if certain conditions hold – for example, that no prior events exist for a subject, or that no changes have occurred since your last read.

Preconditions let you enforce such guarantees without additional roundtrips or manual checks. They support use cases like optimistic locking, subject initialization, and conflict detection in concurrent environments.

How Preconditions Work

Preconditions are evaluated by EventSourcingDB as part of the same transaction that writes your events. If all preconditions are met, the events are written. If any of them fail, nothing is written, and the operation is aborted with HTTP status code 409 Conflict.

You can include preconditions by adding a preconditions array to the request body, alongside the events array.

Available Precondition Types

EventSourcingDB supports the following preconditions:

isSubjectPristine

This precondition checks that a given subject has no events yet. It is typically used when introducing a subject for the first time – for example, when acquiring a book or registering a new user. It ensures that the subject is empty and that no prior write has occurred.

{
  "type": "isSubjectPristine",
  "payload": {
    "subject": "/books/42"
  }
}

Use this precondition when your event is meant to initialize a stream and should not be duplicated.

isSubjectPopulated

This precondition checks that a given subject has at least one event. It is the opposite of isSubjectPristine and is typically used when updating an existing subject – for example, when borrowing a book or updating a user profile. It ensures that the subject exists and has been initialized.

{
  "type": "isSubjectPopulated",
  "payload": {
    "subject": "/books/42"
  }
}

Use this precondition when your event is meant to modify an existing stream and should not operate on uninitialized subjects.

isSubjectOnEventId

This precondition checks that the most recent event for a subject has a specific ID. It is used to implement optimistic concurrency: ensuring that no new events have been written to the subject since you last read it.

{
  "type": "isSubjectOnEventId",
  "payload": {
    "subject": "/books/42",
    "eventId": "23"
  }
}

This is useful when writing a follow-up event that assumes the subject is in a known state. If another event has been added in the meantime, the write will be rejected – preventing lost updates or inconsistent state.

isEventQlQueryTrue

This precondition evaluates an EventQL query and expects the result to be true. It allows you to express arbitrary consistency checks based on the current state of the event store.

{
  "type": "isEventQlQueryTrue",
  "payload": {
    "query": "FROM e IN events WHERE e.data.title == '2001 – A Space Odyssey' PROJECT INTO COUNT() == 0"
  }
}

Use this precondition when other types are insufficient – for example, to enforce uniqueness, detect conflicts, or implement custom business rules declaratively.

Transactional Behavior

All preconditions are evaluated as part of the same transaction as the events. This guarantees atomicity:

  • If all preconditions pass, all events are written.
  • If any precondition fails, no events are written.

This makes preconditions a safe and expressive way to guard against race conditions and enforce domain invariants.

Best Practices

  • Use isSubjectPristine when initializing a subject to prevent accidental re-creation.
  • Use isSubjectPopulated when updating a subject to ensure it has been initialized before applying changes.
  • Use isSubjectOnEventId when updating a subject based on prior reads to ensure consistency.
  • Don't solely rely on preconditions for business logic – use them to enforce technical guarantees, not to replace domain modeling.
  • Keep in mind that preconditions are optional, but highly recommended in collaborative or distributed environments.

Designing with Preconditions

Preconditions help you build safer, more predictable event-sourced systems. They allow the database to participate in consistency checksreducing the need for custom locking logic and race-condition handling in your application.

Used thoughtfully, they provide a clean and reliable foundation for modeling workflows, enforcing invariants, and coordinating concurrent operations.