A TypeScript/Node.js implementation of the Dynamic Consistency Boundary (DCB) pattern for event sourcing, as specified by Sara Pellegrini and Bastian Waidelich at dcb.events.
DCBs replace the traditional aggregate with a more flexible consistency model: instead of locking an entire aggregate root, you define the exact subset of events (by type and tags) that a command depends on, and the event store enforces consistency only within that boundary. This eliminates false conflicts and allows independent commands to execute concurrently even when they share a stream.
See the full DCB specification for the pattern definition.
| Package | npm | Description |
|---|---|---|
@dcb-es/event-store |
@dcb-es/event-store | Core abstractions -- EventStore interface, tags, queries, decision models, in-memory implementation |
@dcb-es/event-store-postgres |
@dcb-es/event-store-postgres | Production Postgres adapter with advisory locking, pg_notify subscriptions, and handler infrastructure |
- Overview -- The DCB pattern, how this library models it, and high-level architecture
- Getting Started -- Installation, project setup, running tests, and debugging
- EventStore Interface --
EventStore,DcbEvent,AppendCondition,AppendConditionError, and the read/append contract - Tags and Queries --
Tags,Query,SequencePosition, and how tag-based filtering drives the consistency boundary - Decision Models --
EventHandler,EventHandlerWithState,buildDecisionModel, and the pattern for deriving command decisions from event history - MemoryEventStore -- In-memory implementation, test utilities, and
streamAllEventsToArray
- Design -- How the Postgres adapter implements DCBs: scoped locking, condition checking, append strategies, notifications
- PostgresEventStore -- Configuration, schema, append strategies,
pg_notifysubscriptions, andsubscribe() - Lock Strategies -- Advisory locks, row-level locks, FNV-1a hashing, and choosing a strategy
- Event Handling --
runHandler,waitUntilProcessed, bookmark management, and building projections
- Examples -- Walkthrough of
course-manager-cliandcourse-manager-cli-with-readmodel - Internals -- Implementation details, design decisions, and schema layout
import { buildDecisionModel, EventHandlerWithState, Tags } from "@dcb-es/event-store"
import { PostgresEventStore } from "@dcb-es/event-store-postgres"
const CourseExists = (courseId: string): EventHandlerWithState<any, boolean> => ({
tagFilter: Tags.fromObj({ courseId }),
init: false,
when: { courseWasRegistered: () => true }
})
// Read events, derive state, get an append condition
const { state, appendCondition } = await buildDecisionModel(eventStore, {
courseExists: CourseExists("cs101")
})
if (state.courseExists) throw new Error("Course already exists")
// Append with the condition -- the store rejects if conflicting events
// were written since the decision was made
await eventStore.append({
events: { type: "courseWasRegistered", tags: Tags.fromObj({ courseId: "cs101" }), data: { title: "CS 101", capacity: 30 }, metadata: {} },
condition: appendCondition
})MIT