Hindsight
Type-safe and evolvable event sourcing for Haskell
Hindsight is a type-safe event sourcing system that provides strong compile-time guarantees for event handling, versioning, and consistency with multiple storage backends.
Hindsight in Action
Type-Safe Event Definition
Define events with compile-time versioning guarantees. No runtime surprises.
-- Event definition
instance Event "user_registered"
-- Event payload
data UserInfo = UserInfo
{ userId :: Text
, email :: Text
} deriving (Generic, FromJSON, ToJSON)
-- Version declaration
type instance MaxVersion UserRegistered = 0
type instance Versions UserRegistered =
'[UserInfo]
-- Migration (automatic for single version)
instance MigrateVersion 0 UserRegisteredBackend-Agnostic Subscriptions
Subscribe to events with handlers that work across all backends.
{-# LANGUAGE RequiredTypeArguments #-}
-- Subscribe to events (works with any backend)
subscribeToUsers :: BackendHandle backend -> IO (SubscriptionHandle backend)
subscribeToUsers store =
subscribe store
( match "user_registered" handleUser :?
MatchEnd )
(EventSelector AllStreams FromBeginning)
where
-- Handler runs for each event
handleUser envelope = do
let user = envelope.payload
putStrLn $ "New user: " <> user.email
return ContinueSQL Projection Handlers
Transform events into queryable read models with ACID guarantees.
{-# LANGUAGE RequiredTypeArguments #-}
-- Projection handler (PostgreSQL transactions)
userProjection :: ProjectionHandler "user_registered" backend
userProjection envelope = do
let user = envelope.payload
-- Execute SQL in transaction
statement () $ Statement sql encoder decoder True
where
sql = "INSERT INTO users (id, email) VALUES ($1, $2)"
encoder = contrazip2
(param (nonNullable text))
(param (nonNullable text))Flexible Backend Choice
Start simple, scale when ready. Same API, different backends.
-- File system store
fsStore :: IO (BackendHandle FilesystemStore)
sqlStore =
newFilesystemStore "./events"
-- PostgreSQL store
sqlStore :: IO (BackendHandle PostgreSQLStore)
sqlStore = do
pool <- createPool postgresSettings
newPostgreSQLStore pool
-- Same operations, different backends
insertEvents devStore Nothing batch
insertEvents sqlStore Nothing batchKey Features
- Type-safe events with automatic upcasts and golden test generation
- Real-time subscriptions for event stream processing
- Multiple backends: Memory, Filesystem, and PostgreSQL
- Projection system for building read models
- Strong consistency and ordering guarantees
Get Started
Check out our documentation