Skip to content

Repository files navigation

Eventium

A Haskell library for building event-sourced applications with CQRS.

Overview

Eventium provides composable, type-safe abstractions for event sourcing: event stores with optimistic concurrency, pure projections, command handlers, process managers, event subscriptions, and pluggable storage backends. It is a modernized fork of eventful, updated for GHC 9.10.

Packages

Package Description
eventium-core Core abstractions: event stores, projections, command handlers, process managers, codecs, schema evolution, TH utilities
eventium-memory STM-based in-memory event store for development and testing
eventium-sqlite SQLite backend via persistent
eventium-postgresql PostgreSQL backend via persistent
eventium-sql-common Shared Persistent entity definitions and SQL operations
eventium-testkit Shared hspec test utilities

Quick Start

# Enter dev environment (requires Nix with flakes)
nix develop

# Build everything
just build

# Run all tests
just test

Using Eventium in your project

Add eventium-core plus a storage backend to your dependencies:

dependencies:
  - eventium-core >= 0.1.0
  - eventium-sqlite >= 0.1.0 # or eventium-postgresql, eventium-memory

Minimal working example

import Eventium
import Eventium.Store.Memory

main :: IO ()
main = do
  tvar <- eventMapTVar
  let writer = runEventStoreWriterUsing atomically (tvarEventStoreWriter tvar)
      reader = runEventStoreReaderUsing atomically (tvarEventStoreReader tvar)

  -- Apply a command through a command handler
  result <- applyCommandHandler writer reader myCommandHandler aggregateId myCommand
  case result of
    Left err -> print err
    Right events -> print events

Key Abstractions

  • Projection -- Pure fold: seed state + event handler. Rebuilds aggregate or read-model state from events.
  • CommandHandler -- Validates a command against current state and produces events or a domain error. applyCommandHandlerWithCache integrates with ProjectionCache for faster aggregate loading.
  • EventStoreReader / EventStoreWriter -- Polymorphic over key, position, monad, and event types. Supports versioned (per-aggregate) and global (cross-aggregate) streams.
  • ProjectionCache -- Snapshot store for aggregate state, avoiding full event replay. snapshotEventHandler auto-updates the cache as events are written.
  • ReadModel -- Abstraction for queryable persistent views driven by the global event stream. Users define schema and event handlers; the library manages checkpointing. rebuildReadModel replays all events for one-shot rebuilds.
  • ProcessManager -- Coordinates long-running workflows across aggregates. Reacts to events with pure [ProcessManagerEffect] values (including IssueCommandWithCompensation for saga compensation).
  • CommandDispatcher -- Routes commands to aggregates and reports CommandDispatchResult (CommandSucceeded | CommandFailed). commandHandlerDispatcher provides list-based multi-aggregate routing.
  • EventHandler -- Composable event consumer with Contravariant, Semigroup, and Monoid instances.
  • EventPublisher -- Decouples post-write notification from the store writer. publishingEventStoreWriter wraps a writer to auto-dispatch after each write.
  • EventSubscription -- Push-based event delivery. pollingSubscription polls the global stream at a configurable interval.
  • Codec -- Bidirectional event encoding/decoding with JSON support and TH-generated sum-type codecs.
  • SchemaEvolution -- Upcast-on-read event schema evolution against an immutable log: a versioned payload envelope plus a registry of pure single-hop upcasters (atKey, addFieldIfAbsent, renameField, removeField). upcastingValueCodec normalizes older stored events to the current shape on read without ever rewriting the log; version-skipping runs more hops.

Examples

Working examples demonstrate increasing complexity:

Counter CLI (examples/counter-cli/)

Minimal single-file example: bounded counter with in-memory store.

cabal run counter-cli

Cafe (examples/cafe/)

Restaurant ordering system (inspired by Edument's CQRS tutorial): tab management, chef todo list as a polling read model.

cabal run cafe-main -- --help
cabal run cafe-chef-todo-main -- --database-path cafe.db

Bank (examples/bank/)

Full CQRS application: accounts, customers, money transfers via process manager, read models, event publishing.

cabal run bank-main -- --help

Build System

Nix + Cabal with GHC 9.10.3. Cabal files are generated from package.yaml via hpack.

just build          # cabal build all
just test           # cabal test all
just hpack          # regenerate .cabal from package.yaml
just format         # ormolu formatting
just ghcid          # continuous compilation

PostgreSQL tests require a running instance (docker compose up -d). See CLAUDE.md for env var details.

Documentation

License

MIT -- see LICENSE.md.

About

Haskell event sourcing library

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages