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 UserRegistered
Backend-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"user_registered" handleUser :?
( match MatchEnd )
EventSelector AllStreams FromBeginning)
(where
-- Handler runs for each event
= do
handleUser envelope let user = envelope.payload
putStrLn $ "New user: " <> user.email
return Continue
SQL Projection Handlers
Transform events into queryable read models with ACID guarantees.
{-# LANGUAGE RequiredTypeArguments #-}
-- Projection handler (PostgreSQL transactions)
userProjection :: ProjectionHandler "user_registered" backend
= do
userProjection envelope let user = envelope.payload
-- Execute SQL in transaction
$ Statement sql encoder decoder True
statement ()
where
= "INSERT INTO users (id, email) VALUES ($1, $2)"
sql = contrazip2
encoder
(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 "./events"
newFilesystemStore
-- PostgreSQL store
sqlStore :: IO (BackendHandle PostgreSQLStore)
= do
sqlStore <- createPool postgresSettings
pool
newPostgreSQLStore pool
-- Same operations, different backends
Nothing batch
insertEvents devStore Nothing batch insertEvents sqlStore
Key Features
- Type-safe events with automatic versioning using DataKinds
- Multiple backends: Memory, Filesystem, and PostgreSQL
- Real-time subscriptions for event stream processing
- Projection system for building read models
- Strong consistency guarantees across all operations
Get Started
Ready to dive in? Check out our comprehensive documentation:
Why Hindsight?
Event sourcing provides powerful guarantees for distributed systems, but implementing it correctly is challenging. Hindsight leverages Haskell’s type system to catch common mistakes at compile time:
- No version mismatches: The type system ensures events can always be deserialized
- No ordering bugs: Causal consistency is enforced through the API
- No silent failures: All error cases are explicit and handled