# 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 declaratively** – **directly 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.

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

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

### `isSubjectPopulated` *(Added in 1.2)*

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.

```json
{
  "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.

```json
{
  "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` *(Added in 1.1)*

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.

```json
{
  "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.
- *(Added in 1.2)* **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 checks** – **reducing 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.
