Alvyn logoAlvyn

Introduction

Alvyn is a production-grade event store for PostgreSQL with type-safe aggregates, event-backed snapshots, GDPR crypto-shredding, projections, transactional outbox, and schema evolution.

Alvyn

Alvyn is an event sourcing library for Node.js and PostgreSQL. It provides everything you need to build event-sourced systems with strong consistency guarantees, GDPR compliance, and full TypeScript support.

Features

  • Type-safe aggregates — Define event maps and evolve handlers with full TypeScript inference via defineAggregate
  • Fan-out subscriptions — Catch up and tail matching events live with resumable cursors and PostgreSQL wake-ups
  • Event-backed snapshots — Cache domain-defined derived state as generated events in the same stream
  • GDPR crypto-shredding — Per-entity AES-256-GCM envelope encryption with key revocation and tombstoned events
  • Projections — Build read models from the global event stream with checkpoint tracking and exactly-once semantics
  • Transactional outbox — At-least-once delivery to external systems, written atomically with events
  • Schema evolution — Read-time upcasters that transform old event shapes without modifying stored data
  • CloudEvents v1.0.2 — All events comply with the CloudEvents specification
  • PostgreSQL native — Advisory locks, OCC, FOR UPDATE SKIP LOCKED, and schema isolation

Quick Start

Installation

npm install @lox-solutions/alvyn pg

Setup

import { Pool } from "pg";
import { EventStore, defineAggregate } from "@lox-solutions/alvyn";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

const eventStore = new EventStore({
  pool,
});

await eventStore.setup(); // idempotent — safe on every startup

Define an Aggregate

type OrderEvents = {
  OrderPlaced: { customerId: string; total: number };
  OrderShipped: { trackingNumber: string };
};

type OrderState = { status: "pending" | "placed" | "shipped"; total: number };

const Order = defineAggregate<OrderState, OrderEvents>()({
  streamPrefix: "Order",
  evolve: {
    OrderPlaced: (state, event) => ({
      ...state,
      status: "placed",
      total: event.data?.total ?? 0,
    }),
    OrderShipped: (state) => ({
      ...state,
      status: "shipped",
    }),
  },
});

Load and Append

// Load aggregate state
const order = await Order.load(eventStore, "order-123");
// order.state  -> { status: "placed", total: 99.99 }
// order.version -> 1

// Append events with optimistic concurrency
await Order.append(eventStore, {
  entityId: "order-123",
  expectedVersion: order.version,
  events: [{ type: "OrderShipped", data: { trackingNumber: "TRACK-456" } }],
});

Requirements

  • Node.js >= 18
  • PostgreSQL >= 14
  • pg (peer dependency)

Next Steps

On this page