ADR-002: Dynamic Consistency Boundaries as the Core Model
Status
Section titled “Status”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.
Context
Section titled “Context”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
expectedStreamVersionmapping (§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.
Decision
Section titled “Decision”Decision 1 — The core interface is DCB
Section titled “Decision 1 — The core interface is 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.
Decision 4 — dcb is reclassified Core
Section titled “Decision 4 — dcb is reclassified Core”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.
Alternatives Considered
Section titled “Alternatives Considered”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.
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- The contradiction between §20.9 and §18c.3 is gone.
- §9 loses the two-notion version model, the batch caveat and the
expectedStreamVersionmapping. 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.
Negative
Section titled “Negative”- 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
tagsrecord 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.