Skip to content

Keeping decisions correct under concurrency

Rules such as “a member can borrow at most three books” and “an email address is used once” are easy to write and easy to get wrong. The usual code reads current state, decides, then appends an event. Those are three separate steps, and another request can run between them.

Two requests both read “two books borrowed”. Both decide that a third is allowed. Both append BookBorrowed. The member now has four books, and neither request did anything wrong by itself.

Checking a read model first does not fix this. A read model is built from events after they are appended, so it can lag behind the event log. Even a read model that is fully up to date describes the state at the moment you read it, not at the moment you append. Under concurrency, a rule holds only if the append itself fails when the facts the decision depended on have changed.

Traditional event sourcing protects a decision by locking one stream or aggregate: the append succeeds only if nobody else wrote to that stream since you loaded it. That works when a rule fits inside one stream. It is too narrow when a rule spans several entities, and too wide when the stream holds events the rule does not care about.

The Dynamic Consistency Boundary (DCB) draws the boundary per decision instead. You name the events the decision depends on, read them, and append with a condition: reject the append if any matching event has been written since that read. Only events that matter to the decision cause a conflict. Unrelated writes, even to the same entity, do not.

Chronicle supports DCB-style decision models and append conditions within its own event model. It is not a DCB event store, and the difference shapes how you model a rule.

DCBChronicle
One event can carry tags for several entitiesEach event belongs to exactly one event source
Tags select the events a decision depends onStream metadata does the same job: event source id, event source type, event stream type and event stream id
A query item combines event types and tagsA concurrency scope combines event types and those metadata dimensions
Several query items in one append conditionSeveral scopes, one per event source, checked in one atomic append

An event source definition is the typed way to declare those dimensions: what an event source is, which streams it has, and which dimensions guard an append to each stream. See event sources and DCB. A guard derived from a definition covers every event in that stream, whatever its type, so it acts like an aggregate boundary. When a rule depends on only some events, use a decision read.

A rule that spans two entities is written as one fact per entity, each with its own event type, appended together. For example, a course subscription appends a seat-taken event to the course and a joined-course event to the student. DCB would record one event tagged with both. The canonical DCB examples show the Chronicle answer for each case.

Pick the lightest tool that makes the rule hold.

  • Constraints enforce uniqueness. Chronicle checks them as part of the append, so two requests cannot both claim the same value.
  • Decision reads and protected decisions guard a rule over state on one or more keys. The decision reads state in a way that records where in the event log it read, and the append fails if a relevant event arrived after that point.
  • Stream decisions and aggregates guard a rule by the events of a stream or of an aggregate’s event source. The append fails if a relevant event was appended after you loaded it: any event in the stream for a stream decision, or an event of a type the aggregate handles on its event source.
  • Repair accepts that a rare violation can happen. You detect it afterwards and compensate with a new event. Use it only when a short-lived violation is acceptable to the business.

A guarded decision that loses the race is rejected, not retried for you. The caller reads the new state and resubmits deliberately.

Choose by how you work with Cratis.

You are usingGo to
The Chronicle client directlyChoosing a consistency approach in Chronicle
Arc commandsConsistency in Arc
Screenplay modelsWhat reads guarantees
Event source definitions and streamsEvent sources and, for Arc commands, event source definitions

For the ideas underneath, see what is event sourcing? and event modeling.