- Node.js 18+
- TypeScript 5+
- PostgreSQL 14+ (for the Postgres adapter)
Install the core event store abstractions:
npm install @dcb-es/event-storeFor the Postgres-backed implementation:
npm install @dcb-es/event-store-postgres@dcb-es/event-store provides the EventStore interface, tag-based querying, buildDecisionModel, and an in-memory implementation suitable for testing. @dcb-es/event-store-postgres provides a production-ready Postgres implementation and utilities such as runHandler, subscribe, and waitUntilProcessed.
- Node.js 18+
- Yarn (classic, v1)
- Docker (required for Postgres tests via testcontainers)
git clone [email protected]:sennentech/dcb-event-store.git
cd dcb-event-store
yarn installThis is a Lerna monorepo with Yarn workspaces. yarn install at the root resolves all inter-package dependencies.
npm run buildRuns tsc in each package via Lerna. You must build before running tests in packages that depend on other packages (e.g. event-store-postgres depends on event-store).
npm testRuns vitest run in every package and example via Lerna.
To run tests for a single package:
cd packages/event-store && npm test
cd packages/event-store-postgres && npm testDocker must be running for any package that uses Postgres tests (event-store-postgres, both examples). The test suite uses testcontainers to start a Postgres 17 container automatically.
npm run lint
npm run lint-fixESLint + Prettier across all packages.
Packages that need Postgres share two pieces of test infrastructure in the root test/ directory:
test/vitest.globalSetup.ts -- Starts a single Postgres 17 container (via @testcontainers/postgresql) before all tests in the package and tears it down after. The connection URI is exported as process.env.__PG_CONNECTION_URI.
test/testPgDbPool.ts -- Creates a fresh, isolated database per test suite. Each call to getTestPgDatabasePool() creates a new database on the shared container and returns a pg.Pool connected to it. This means test suites run in parallel without interfering with each other.
These are imported using a Vite resolve alias. Each package's vitest.config.ts maps @test to the root test/ directory:
resolve: {
alias: {
"@test": path.resolve(__dirname, "../../test")
}
}So test files import with:
import { getTestPgDatabasePool } from "@test/testPgDbPool"Both examples implement the DCB course-subscriptions example -- a domain where students subscribe to courses with capacity limits and subscription caps.
Both require a running PostgreSQL instance. The simplest way:
docker run -d \
--name dcb-postgres \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
postgres:17Then create the database the examples expect:
docker exec -it dcb-postgres psql -U postgres -c "CREATE DATABASE dcb_test_1;"Both examples connect to localhost:5432 with user postgres, password postgres, database dcb_test_1.
An interactive CLI for managing courses and student subscriptions. All reads go through the event store directly using buildDecisionModel -- there is no separate read model.
npm run build
cd examples/course-manager-cli
npx ts-node index.tsThe CLI presents a menu for registering courses, registering students, subscribing/unsubscribing students, and updating course capacity and titles.
Extends the basic example with a Postgres-backed read model projection. Commands still use buildDecisionModel for decision-making, but queries (find course, find student) read from a projected Postgres table. The projection runs as a background handler using runHandler and subscribe, processing events via pg_notify as they are appended.
npm run build
cd examples/course-manager-cli-with-readmodel
npx ts-node index.tsOn startup, this example:
- Initialises the event store schema (
ensureInstalled) - Installs the handler bookmarks table (
ensureHandlersInstalled) - Installs the course-subscriptions read model tables
- Starts the projection handler in the background
- Launches the interactive CLI
Run a single test file with vitest:
cd packages/event-store-postgres
npx vitest run src/PostgresEventStore.tests.tsFor verbose output:
npx vitest run --reporter=verbose src/PostgresEventStore.tests.tsWatch mode re-runs on file changes:
npx vitest src/PostgresEventStore.tests.tsTests fail with "PG container not started" Docker is not running, or the testcontainers Postgres container failed to start. Make sure Docker Desktop (or the Docker daemon) is running before running tests.
Module not found errors in dependent packages
Run npm run build from the repo root before testing. event-store-postgres and the examples import the compiled output of event-store -- if packages/event-store/dist/ does not exist, imports will fail.
Tests hang or time out
Testcontainers pulls the postgres:17 image on first run, which can take a while on slow connections. Subsequent runs use the cached image. If tests hang indefinitely, check Docker container logs:
docker ps -a | grep testcontainers
docker logs <container-id>Port 5432 already in use (examples only)
The examples connect to localhost:5432. If another Postgres instance is using that port, stop it or change the port in the example's index.ts. This does not affect the test suite -- tests use testcontainers on a random port.