Skip to content

ADR-002: Dynamic Consistency Boundaries as the Core Model

Accepted — 2026-09-29 (decided by the maintainer, Tobias Brennecke)

Supersedes the dcb row of ADR-001 Decision 1, which classified that bounded context as Supporting. ADR-001 stands as the record of what was decided on 2026-07-24; this document records the later decision rather than amending it.

ADR-001 settled that the global stream (§20) is mandatory for conformance (C22). The specification family nonetheless kept Dynamic Consistency Boundaries — the model published at https://dcb.events/ — as one of five optional, individually claimable extensions (docs/extensions/dcb.md, §18b and §18c), and kept the stream-based append / read pair as the core interface (§10.0).

That arrangement carried a contradiction the specification stated against itself:

  • §20.9 recorded that under the mandatory global stream the DCB consistency check “becomes precise” and that “the per-tag anchor mechanism (§18c.2/§18c.3) is unnecessary when one authoritative remote serializes.”
  • §18c.3 nonetheless required that mechanism: “The conditional append MUST use Git’s atomic multi-ref transaction … verify each queried tag ref at its read-time tip.”
  • Because C22 gives every conforming store a sequencer, the §20.9 condition always holds. The core therefore mandated machinery it had already declared superfluous.

Three further facts made the extension framing hard to defend:

  • §20.2 already described state streams as secondary views over the global stream, and tags as lean indices over the same chain. A stream and a tag were already two representations of one selection, differing in §20.8.1’s full-chain-versus-lean-index choice rather than in model.
  • §20.4.1 already required every append to advance the global stream, the state stream and any tag index in one atomic multi-ref transaction — which is precisely the primitive §18c.3 named as DCB’s enabler.
  • §9 required implementations to carry two parallel notions of version (the tip commit SHA as compare-and-swap guard, the integer stream version as contract, §9.1.3), plus a batch caveat (§9.3.1) and a three-case expectedStreamVersion mapping (§9.2.3). An append condition needs none of that apparatus: one query and one sequence position.

§18 already recorded that DCB has implementations beyond this project — Marten, and the eventsourcing library — and that EventSourcingDB offers query-based preconditions, which is the same idea under another name. Emmett, the reference binding, is the one that does not implement DCB.

The core operations are append(events, condition?) and read(query, options?) (§10.0). The append condition (§18c.1) — a query plus an after sequence position — is the sole write authorization (§9.1.1). §18b and §18c move from docs/extensions/dcb.md into the core specification, and that extension document is removed. A new conformance clause C23 requires the interface. The specification moves from v0.21.0 to v0.22.0.

Decision 2 — The stream view is derived and stays mandatory

Section titled “Decision 2 — The stream view is derived and stays mandatory”

A state stream is a materialized query: the events carrying one state id, maintained as a secondary commit chain under refs/ges/streams/<encoded> (§20.2). A read of the query selecting that state id and a walk of that state stream must return the same events in the same order (§10.0). Every conforming store maintains it under C22.

The view is kept rather than derived on demand for two reasons. Folding one entity’s history walks a short chain instead of filtering the global stream. And the Emmett binding needs it: Emmett is stream-based, so extensions/emmett-binding.md binds the view, not the core, and reaches only the subset of the core expressible as a single-state-id append condition.

Decision 3 — The per-tag anchor mechanism is withdrawn

Section titled “Decision 3 — The per-tag anchor mechanism is withdrawn”

An append condition is evaluated against the global stream and committed in the atomic multi-ref transaction of §20.4.1. The per-tag anchor refs of the former §18c.2 and §18c.3 are gone. With one authoritative remote serializing, the check is exact: a conjunctive or typed query conflicts only with an event matching every term (§18c.4, E-18c-5), where the per-tag approximation produced false conflicts.

On the ADR-001 core domain chart, the dcb bounded context moves from Supporting to Core. It is no longer a conformist translation of an outside vocabulary into an optional layer; it is the interface every conforming store exposes. The one-way dependency ADR-001 recorded still holds: DCB’s global enforcement requires an authority, never the reverse.

Leave DCB an optional extension and repair only the contradiction. Rewrite §18c.3 to name the global-stream check and delete the anchor refs, keeping the stream model as core. This was the maintainer’s first recommendation. It is the smaller change and it preserves the O(1) expected-version check that §9.2.1 gives a stream append. It was rejected because it keeps two write models where the substrate carries one, and because it leaves the core unable to express a boundary spanning two state ids — the capability DCB exists to add.

Make DCB core and delete the stream view entirely. Pure DCB has no per-state-id stream. Rejected: it discards the short-chain fold, it strands the Emmett binding, and §20.2 already commits the store to maintaining state streams as secondary views.

Introduce a second conformance profile so a store could claim either model. Rejected: R16 in ges-review-and-ears.md already notes that §16 lacks named profiles; adding one here would multiply the interop surface the family exists to narrow.

  • The contradiction between §20.9 and §18c.3 is gone.
  • §9 loses the two-notion version model, the batch caveat and the expectedStreamVersion mapping. The mapping moves to the Emmett binding as §10.1.1, where it belongs.
  • A consistency boundary may span state ids, which the stream model could not express.
  • The conjunctive-query precision limit of the former §18c.4 is gone; the check is exact.
  • The core binds to a published, stable interface. The former core bound to Emmett’s EventStore, which the specification twice notes must be re-verified against the installed version because Emmett is pre-1.0.
  • The write path costs more. An expected-version check was one ref read (§9.2.1); an append condition costs a scan of the events after after (§9.2.4). §9.2.2 permits evaluation against a tag index, provided that index advances inside the same transaction, and §18b.3 makes the index mandatory.
  • Every event must now carry a tag identifying its state stream (§18b.4), so the tags record field changed from MAY to MUST. Records written under v0.21.0 lack it.
  • A new trailer GES-Sequence (§7.3.1) is needed to read a sequence position in O(1), and a rebase must rewrite it (§20.7.1).
  • The Emmett binding reaches a strict subset of the core. A consumer needing a cross-state-id boundary cannot get one through Emmett.

Git Event Store is an independent open specification for an event store that uses Git as its storage component. It is not affiliated with or endorsed by the Git Project or Software Freedom Conservancy. Git and the Git logo are either registered trademarks or trademarks of Software Freedom Conservancy, Inc., corporate home of the Git Project, in the United States and/or other countries.

Git Event Store is not affiliated with or endorsed by Kurrent, the maker of KurrentDB (formerly EventStoreDB). "Event store" is used here as the generic name of the event-sourcing storage pattern.