# Dynamic Consistency Boundaries (DCBs)

Event-sourced systems often use [**aggregates**](/concepts/aggregates) to define where consistency must be preserved. Aggregates help prevent conflicting updates and capture business invariants. But they also introduce rigidity: Once an aggregate boundary is defined, it becomes difficult to change – even when the domain evolves or the original design turns out to be insufficient.

**Dynamic Consistency Boundaries (DCBs)** offer a more flexible alternative. Instead of defining consistency boundaries upfront in the domain model, DCBs let you enforce consistency **at runtime**, based on the actual state of the event store and the specific decision you're about to make. The key idea is simple: **Consistency becomes a condition, not a structure.**

## How It Works

DCBs rely on **queries** to express what consistency means in a given context. The process typically follows three steps:

1. You start by running a query that selects the set of events relevant for the current decision. This is effectively an **ad-hoc aggregate**: it groups just the data you need, for just this one decision.
2. Based on the selected events, you apply **business logic** in your application. For example, you might check whether a reader is allowed to borrow another book.
3. When you write the resulting event, you attach a **condition** that makes the event store re-evaluate the query and ensure that **the same invariant still holds** – that is, that nothing has changed since the decision was made.

This pattern allows you to define consistency **dynamically**, depending on event content, history, or relationships – without requiring fixed subject boundaries or predefined aggregates.

For example, a library might allow a reader to borrow **at most three books** at a time. The query selects the books the reader has borrowed, the application decides whether another one is allowed, and the condition ensures that the reader has still borrowed **fewer than three books** when the new event is written. If they've already borrowed three or more by then, the write is rejected.

> **Try it with EventSourcingDB**
>
> In EventSourcingDB, the query is written in **[EventQL](/docs/eventsourcingdb/eventql)**, and the condition is a precondition of type **[`isEventQlQueryTrue`](/docs/eventsourcingdb/preconditions)**:
>
> ```json
> {
>   "type": "isEventQlQueryTrue",
>   "payload": {
>     "query": "FROM e IN events WHERE e.type == 'io.eventsourcingdb.library.book-borrowed' AND e.data.borrowedBy == '/readers/23' PROJECT INTO COUNT() < 3"
>   }
> }
> ```
>
> This precondition ensures that the reader identified by `/readers/23` has borrowed **fewer than three books**.

> **Simplified Example**
>
> This is a **simplified example**: it doesn't account for returned books or due dates. But it shows the general idea – the **consistency rule (invariant)** is enforced dynamically, based on the reader's current borrowing history. The same query must be run twice: first to make the decision (can this book be borrowed?), and again at write time to confirm that the underlying event set hasn't changed.

## Trade-offs and Considerations

DCBs offer flexibility – but not for free. Unlike subject-based reads, such queries are **more expensive to evaluate**, especially when scanning large datasets. And since DCBs require **two query passes** – one during decision-making, and one as a precondition – the cost effectively doubles.

For this reason, DCBs are best used when:

- Static aggregates would be too coarse or too inflexible,
- The consistency rules change over time or depend on data content,
- Or the relevant events span multiple subjects or types.

In such cases, DCBs can reduce complexity, improve expressiveness, and avoid workarounds like synthetic aggregates or distributed locks. But in performance-sensitive paths, traditional subject-based aggregates may still be the better fit.

## Aggregates and DCBs in Context

DCBs and aggregates are **complementary tools**. Aggregates define **design-time consistency boundaries** that reflect stable, subject-local rules. DCBs define **runtime consistency conditions** that adapt to the actual structure of decisions and data.

You can – and often should – use both:

- Use aggregates when you can cleanly define them, and when invariants are naturally tied to subjects.
- Use DCBs when consistency depends on a broader context, or when you want to enforce rules declaratively based on event history.

This flexibility lets you evolve your consistency model gradually – without locking yourself into rigid structures too early.

## Further Reading

The concept of dynamic consistency boundaries is discussed in more depth at **[dcb.events](https://dcb.events/)**.
