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 module when no event store is given in load / store methods parameters.
To use RailsEventStore add to Gemfile:
gem 'rails_event_store'
Then setup RailsEventStore as described in the docs
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
# ... more later
end
Define aggregate logic
OrderSubmitted = Class.new(RailsEventStore::Event)
OrderExpired = Class.new(RailsEventStore::Event)
class Order
include AggregateRoot
HasBeenAlreadySubmitted = Class.new(StandardError)
HasExpired = Class.new(StandardError)
def initialize
self.state = :new
# any other code here
end
def submit
raise HasBeenAlreadySubmitted if state == :submitted
raise HasExpired if state == :expired
apply OrderSubmitted.new(data: {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
stream_name = "Order$123"
order = Order.new.load(stream_name)
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
stream_name = "Order$123"
order = Order.new.load(stream_name)
order.submit
order.store
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. If stream_name is not specified events will be stored
in the same stream from which order has been loaded.
Resources
There're already few blog posts about building an event sourced applications with 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