EventStoreRuby
A comprehensive Ruby implementation of event sourcing with real-time event subscriptions and projections. This system provides persistent event storage with automatic notification to subscribers for building responsive, event-sourced applications.
This library is based on the @ricofritzsche/eventstore TypeScript library and it provides the same event-sourcing primitives, including:
-
EventStore implementations
-
EventStoreRuby::MemoryEventStore
– fully in-memory (great in tests) -
EventStoreRuby::PostgresEventStore
– persistent, PostgreSQL-backed
-
-
Notifier –
MemoryEventStreamNotifier
for real-time fan-out of appended events - Query engine – powerful payload-predicate matching identical to the original TS implementation
-
Optimistic locking – conditional
append
withexpected_max_sequence_number
Installation
Add to your Gemfile:
gem 'eventstore_ruby', '~> 0.1.1'
or:
$ gem install eventstore_ruby
Quick start (Memory)
require 'eventstore_ruby'
store = EventStoreRuby::MemoryEventStore.new
# Subscribe **before** writing so you get real-time notifications
subscription = store.subscribe do |events|
puts "got #{events.size} event(s): #{events.map(&:event_type).join(', ')}"
end
# Append events
store.append([
EventStoreRuby::Event.new(event_type: 'UserRegistered', payload: {user_id: 1}),
EventStoreRuby::Event.new(event_type: 'EmailVerified', payload: {user_id: 1})
])
# Historical query
filter = EventStoreRuby.create_filter(%w[UserRegistered EmailVerified])
result = store.query(filter)
puts "history has #{result.events.size} events, max seq ##{result.max_sequence_number}"
subscription.unsubscribe
Quick start (PostgreSQL)
# launch Postgres
$ docker run --name eventstore-pg -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:15
$ export DATABASE_URL="postgres://postgres:postgres@localhost:5432/postgres"
require 'eventstore_ruby'
store = EventStoreRuby::PostgresEventStore.new
store.initialize_database # creates table & indexes on first run
# same API as memory store …
API overview
# Building filters
filter = EventStoreRuby.create_filter(%w[UserRegistered], [{user_id: 1}])
# Querying
qr = store.query(filter) # => QueryResult (events + max_sequence_number)
# Combining multiple filters with OR – EventQuery
Often you need to fetch events that match **any** of several filter criteria.
Instead of doing multiple round-trips you can combine `EventFilter`s into a single
`EventQuery` (logical OR):
```ruby
# Build individual filters
f_users = EventStoreRuby.create_filter(['UserRegistered'])
f_orders = EventStoreRuby.create_filter(['OrderPlaced'], [{country: 'DE'}])
# Combine them
query = EventStoreRuby.create_query([f_users, f_orders])
# Works with both query and append
result = store.query(query)
# Conditional write based on the same logical context
store.append(new_events, query, expected_max_sequence_number: result.max_sequence_number)
EventQuery
is supported by both the in-memory and PostgreSQL stores. Under
the hood it is translated into OR
conditions in SQL for Postgres, and on the
in-memory store it simply checks each contained filter one after another.
Conditional append (optimistic locking)
store.append(events, filter, expected_max_sequence_number: qr.max_sequence_number)
Subscriptions
sub = store.subscribe do |events| … end sub.unsubscribe
For detailed objects & methods see the YARD docs or the source under `lib/eventstore_ruby/`.
## Development / Tests
```bash
$ bundle install
$ rake test # runs Minitest suite (ports of all original Jest specs)
License
MIT © 2024 – originally by Ralf Westphal & Rico Fritzsche. Ruby port by Gazpacho Dev team (Manuel Ortega).