AggregateRoot
Event sourced (with Rails Event Store) aggregate root implementation.
Installation
- Add following line to your application's Gemfile:
gem 'aggregate_root'
Before use
Choose your weapon now! Ekhm I mean choose your event store client. To do so add configuration in environment setup. Example using RailsEventStore:
AggregateRoot.configure do |config|
config.default_event_store = RailsEventStore::Client.new
end
Remember that this is only a default event store used by AggregateRoot::Repository when no event store is given in constructor parameters.
You could always set any event store client (must match interface) when creating AggregateRoot::Repository.
repository = AggregateRoot::Repository.new(YourOwnEventStore.new)
# do you work here...
To use RailsEventStore add to Gemfile:
gem 'rails_event_store'
Then setup RailsEventStore as described in Installation section of readme.
Usage
To create a new aggregate domain object include AggregateRoot::Base module.
It is important to assign id at initializer - it will be used as a event store stream name.
class Order
include AggregateRoot::Base
def initialize(id = generate_id)
self.id = id
# any other code here
end
# ... more later
end
Define aggregate logic
OrderSubmitted = Class.new(RailsEventStore::Event)
OrderExpired = Class.new(RailsEventStore::Event)
class Order
include AggregateRoot::Base
HasBeenAlreadySubmitted = Class.new(StandardError)
HasExpired = Class.new(StandardError)
def initialize(id = generate_id)
self.id = id
self.state = :new
# any other code here
end
def submit
raise HasBeenAlreadySubmitted if state == :submitted
raise HasExpired if state == :expired
apply OrderSubmitted.new(delivery_date: Time.now + 24.hours)
end
def expire
apply OrderExpired.new
end
private
attr_accessor :state
def apply_order_submitted(event)
self.state = :submitted
end
def apply_order_expired(event)
self.state = :expired
end
end
Loading an aggregate root object from event store
repository = ArggregateRoot::Repository.new
order = Order.new(ORDER_ID)
repository.load(order)
Load gets all domain event stored for the aggregate in event store and apply them in order to given aggregate to rebuild aggregate's state.
Storing an aggregate root's changes in event store
repository = ArggregateRoot::Repository.new
order = Order.new(ORDER_ID)
repository.load(order)
order.submit
repository.store(order)
Store gets all unpublished aggregate's domain events (created by executing a domain logic method like submit)
and publish them in order of creation to event store.
Resources
There're already few blogposts about building an event sourced applications wth rails_event_store and aggregate_root gems:
- Why use Event Sourcing
- The Event Store for Rails developers
- Fast introduction to Event Sourcing for Ruby programmers
- Building an Event Sourced application using rails_event_store
- Using domain events as success/failure messages
- Subscribing for events in rails_event_store
- Testing an Event Sourced application
- Testing Event Sourced application - the read side
- One event to rule them all
About

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.