Ruby
meets
Event Sourcing
by @antonpaisov
Event Sourcing
There’s a Person who has an Address
We need to change the address
Event Sourcing
There’s a Person who has an Address
We need to change the address
CRUD: we just remove old one and add a new one
Event Sourcing
There’s a Person who has an Address
We need to change the address
CRUD: we just remove old one and add a new one
ES: publish event about this change and then apply it
Back to CRUD
CRUD may hurt business
CRUD may hurt business
Approaching development from business perspective
CRUD may hurt business
Approaching development from business perspective
Business avoids updates
CRUD may hurt business
Approaching development from business perspective
Business avoids updates
Business never deletes
CRUD may hurt business
Approaching development from business perspective
Business avoids updates
Business never deletes
Business loves audit
What’s the most popular
example of ES system
among developers?
GIT!
+
Audit
Debug
Historic State
Alternative Histories
Distribution support
(Microservices)
In-Memory processing
http://bit.ly/memory-img
IMPORTANT:
Event Sourcing
does not mean
“must be done ASYNC”
Also important
Can’t do proper ES without identify relevant domain
state changes. (from the business point of view)
Look for Events that affect domain state and activity.
Example with
implementation
Shopping cart
Possible to add/remove products from the cart
New request: we need to know what was removed
gem 'rails_event_store'
http://bit.ly/cart-es
module Events
ItemRemovedFromBasket = Class.new(RailsEventStore::Event)
end
lib/events.rb
module EventStoreSetup
def event_store
@event_store ||= RailsEventStore::Client.new.tap do |es|
es.subscribe(Denormalizers::ItemRemovedFromBasket.new,
[Events::ItemRemovedFromBasket])
end
end
end
lib/event_store_setup.rb
app/controllers/orders_controller.rb
def remove_item
cmd = Command::RemoveItemFromBasket.new(product_params)
execute(cmd)
head :ok
end
lib/command/remove_item_to_basket.rb
module Command
class RemoveItemFromBasket < Base
attr_accessor :order_id
attr_accessor :product_id
validates :order_id, presence: true
validates :product_id, presence: true
alias :aggregate_id :order_id
end
end
lib/command/execute.rb
def handler_for(command)
{
Command::RemoveItemFromBasket =>
CommandHandlers::RemoveItemFromBasket,
}.fetch(command.class)
end
lib/command_handlers/remove_item_from_basket.rb
module CommandHandlers
class RemoveItemFromBasket < Command::Handler
def call(command)
with_aggregate(command.aggregate_id) do |order|
order.remove_item(command.product_id)
end
end
private
def aggregate_class
Domain::Order
end
end
end
lib/domain/order.rb
def remove_item(product_id)
apply Events::ItemRemovedFromBasket.new
(data: {order_id: id, product_id: product_id})
end
def apply_events_item_removed_from_basket(event)
order_line.decrease_quantity
remove_order_line(order_line) if order_line.empty?
end
lib/denormalizers/item_removed_from_basket.rb
module Denormalizers
class ItemRemovedFromBasket
def call(event)
item = find(event.data.order_id, event.data.product_id)
item.quantity -= 1
item.quantity > 0 ? item.save! : item.destroy!
end
...
end
end
On Deck
Add Projection usage examples
Approaches
Approaches
New projects: Event Sourcing
Approaches
New projects: Event Sourcing
Legacy: Event Publishing
Conclusions
Help you not to loose data
Clean, testable structure with domain events
Shines with CQRS
May get handy when working on Legacy apps
Materials
Robert Pankowecki about Saga pattern:
http://bit.ly/robert-saga
Materials
Great article about ES from business perspective:
http://bit.ly/abstract-es
Greg Young’s talk recording: http://bit.ly/greg-es
Martin Fowler has a lot of content on the topic
http://docs.geteventstore.com
Arkency blogposts on
ES and DDD in Ruby
http://blog.arkency.com/tags/eventstore/
http://blog.arkency.com/tags/ddd/
http://bit.ly/feedback-anton
Feedback appreciated!

"Ruby meets Event Sourcing" by Anton Paisov