Build Status Gem Version Code Climate Test Coverage Join the chat at https://gitter.im/arkency/rails_event_store

EventStore

A Ruby implementation of an EventStore based on Active Record.

Installation

  • Add following line to your application's Gemfile:
gem 'rails_event_store'
  • Use provided task to generate a table to store events in you DB.
rails generate rails_event_store:migrate
rake db:migrate

Usage

To communicate with ES you have to create instance of RailsEventStore::Client class.

client = RailsEventStore::Client.new

Creating new event

Firstly you have to define own event model extending RailsEventStore::Event class.

class OrderCreated < RailsEventStore::Event
end

# or

OrderCreated = Class.new(RailsEventStore::Event)
stream_name = "order_1"
event = OrderCreated.new(
          data: "sample",
          event_id: "b2d506fd-409d-4ec7-b02f-c6d2295c7edd"
        )#publishing event for specific stream

client.publish_event(event, stream_name)

#publishing global event. In this case stream_name is 'all'.
client.publish_event(event)

Creating new event with optimistic locking:

class OrderCreated < RailsEventStore::Event
end
stream_name = "order_1"
event = OrderCreated.new(
          data: "sample",
          event_id: "b2d506fd-409d-4ec7-b02f-c6d2295c7edd"
        )
expected_version = "850c347f-423a-4158-a5ce-b885396c5b73" #last event_id
client.publish_event(event, stream_name, expected_version)

Reading stream's events forward in batch - starting from first event

stream_name = "order_1"
count = 40
client.read_events_forward(stream_name, :head, count)

In this case :head means first event of the stream.

Reading stream's events forward in batch - staring from given event

# last_read_event is any domain event read or published by rails_event_store

stream_name = "order_1"
start = last_read_event.event_id
count = 40
client.read_events_forward(stream_name, start, count)

Reading stream's events backward in batch

As in examples above, just use read_events_backward instead of read_events_forward. In this case :head means last event of the stream.

Reading all events from stream forward

This method allows us to load all stream's events ascending.

stream_name = "order_1"
client.read_stream_events_forward(stream_name)

Reading all events from stream forward

This method allows us to load all stream's events descending.

stream_name = "order_1"
client.read_stream_events_backward(stream_name)

Reading all events forward

This method allows us to load all stored events ascending.

This will read first 100 domain events stored in event store.

client.read_all_streams_forward(:head, 100)

When not specified it reads events starting from :head (first domain event stored in event store) and reads up to RailsEventStore::PAGE_SIZE domain events.

client.read_all_streams_forward

You could also read batch of domain events starting from any read or published event.

client.read_all_streams_forward(last_read_event.event_id, 100)

Reading all events backward

This method allows us to load all stored events descending.

This will read last 100 domain events stored in event store.

client.read_all_streams_backward(:head, 100)

When not specified it reads events starting from :head (last domain event stored in event store) and reads up to RailsEventStore::PAGE_SIZE domain events.

client.read_all_streams_backward

Deleting stream

You can permanently delete all events from a specific stream. Use this wisely.

stream_name = "product_1"
client.delete_stream(stream_name)

Subscribing to events

To listen on specific events synchronously you have to create subscriber representation. The only requirement is that subscriber class has to implement the 'handle_event(event)' method.

class InvoiceReadModel
  def handle_event(event)
    #we deal here with event's data
  end
end
  • You can subscribe on specific set of events
invoice = InvoiceReadModel.new
client.subscribe(invoice, ['PriceChanged', 'ProductAdded'])
  • You can also listen on all incoming events
invoice = InvoiceReadModel.new
client.subscribe_to_all_events(invoice)

Building an event sourced application with RailsEventStore gem

ArrgegateRoot module & AggregateReporitory have been extracted from RailsEventStore to separate gem. See aggregate_root gem readme to find help how to start. Also this example might be useful.

Resources

There're already few blogposts about Rails EventStore. Check them out:

About

Arkency

Rails Event Store is funded and maintained by Arkency. Check out our other open-source projects.

You can also hire us or read our blog.