Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Events. Events. Events!

Events. Events. Events!

My talk about event sourcing in ruby from wrocloverb (https://wrocloverb.com)

Anton Davydov

March 24, 2019
Tweet

More Decks by Anton Davydov

Other Decks in Programming

Transcript

  1. class State def method_missing(method, *args, &block) eval(method.to_s.capitalize).send(:new) end end class

    Created < State; end class Open < State; end class Closed < State; end
  2. • Software developer at Hippo • OpenSource evangelist • Hanami

    core developer • Try to make own product • Technical consulting
  3. • Coffee • Beer • Psychology • SW-2039-0208-4228 • How

    to draw images for presentations • Bad stories
  4. • Coffee • Beer • Psychology • SW-2039-0208-4228 • How

    to draw images for presentations • Bad stories
  5. Git

  6. Event stream: bus#1 Event stream: bus#1 Event stream: bus#2 Event

    stream: bus#1 Event stream: bus#2 Event stream: bus#2
  7. ES -> event happened and I store it ED ->

    event happened and I say everyone it
  8. • You don’t work with tables you work with events

    and changes • Experemental data structures • Easy to change database implementations
  9. • Hard for understand idea and complicated abstraction • Not

    popular in ruby • Developers need deprogramming
  10. • Harder to get state than CRUD • Hard to

    understand the whole chain of events • Another architecture type
 with different DB structure
  11. events.subscribe('user.created') do |payload| puts payload end events.subscribe('user.*') do |payload| puts

    payload end events.subscribe(/\Auser\..*/) do |payload| puts payload end Subscriber
  12. module Web::Controllers::CartItem class Create include Web::Action def call(params) halt(400) unless

    params.valid? if cart_repo.item_exist?(params[:item]) item = CartItemRepository.new.create_copy(params[:item]) else item = CartItemRepository.new.create(params[:item]) end analytics.call('Item Added') redirect_to routes.order_path(params[:order_id]) end end end
  13. module Web::Controllers::CartItem class Create include Web::Action def call(params) halt(400) unless

    params.valid? event_store.apply(Order::Events::ItemAdded.new(payload)) if cart_repo.item_exist?(params[:item]) item = CartItemRepository.new.create_copy(params[:item]) else item = CartItemRepository.new.create(params[:item]) end analytics.call('Item Added') redirect_to routes.order_path(params[:order_id]) end end end
  14. module Web::Controllers::CartItem class Create include Web::Action def call(params) halt(400) unless

    params.valid? event_store.apply(Orders::Events::ItemAdded.new(payload)) if cart_repo.item_exist?(params[:item]) item = CartItemRepository.new.create_copy(params[:item]) else item = CartItemRepository.new.create(params[:item]) end analytics.call('Item Added') redirect_to routes.order_path(params[:order_id]) end end end
  15. module Web::Controllers::CartItem class Create include Web::Action def call(params) halt(400) unless

    params.valid? events = event_store.get_stream(params[:order_id]) items = @project.call(Order::Projections::ItemList, events) if items.include?(params[:item]) event_store.apply(Orders::Events::ItemAdded.new(payload)) else event_store.apply(Orders::Events::CoppyItemAdded.new(payload)) end analytics.call('Item Added') redirect_to routes.order_path(params[:order_id]) end end end
  16. module Web::Controllers::CartItem class Create include Web::Action def call(params) halt(400) unless

    params.valid? events = event_store.get_stream(params[:order_id]) items = @project.call(Order::Projections::ItemList, events, {}) if items.include?(params[:item]) event_store.apply(Orders::Events::ItemAdded.new(payload)) else event_store.apply(Orders::Events::CoppyItemAdded.new(payload)) end analytics.call('Item Added') redirect_to routes.order_path(params[:order_id]) end end end
  17. module Projections class AllTask def call(event, state) case event when

    Orders::Events::ItemAdded state[:orders] ||= [] state[:orders] << event.payload when Orders::Events::ItemRemoved # ... end state end end end
  18. module Projections class AllTask def call(event, state) case event when

    Orders::Events::ItemAdded state[:orders] ||= [] state[:orders] << event.payload when Orders::Events::ItemRemoved # ... end state end end end
  19. module Projections class AllTask def call(event, state) case event when

    Orders::Events::ItemAdded state[:orders] ||= [] state[:orders] << event.payload when Orders::Events::ItemRemoved # ... end state end end end
  20. module Web::Controllers::CartItem class Create include Web::Action def call(params) halt(400) unless

    params.valid? events = event_store.get_stream(params[:order_id]) items = @project.call(Order::Projections::ItemList, events, {}) if items.include?(params[:item]) event_store.apply(Orders::Events::ItemAdded.new(payload)) else event_store.apply(Orders::Events::CoppyItemAdded.new(payload)) end analytics.call('Item Added') redirect_to routes.order_path(params[:order_id]) end end end
  21. # analytics subscribers event_store.subscribe(Orders::Events::ItemAdded) do |event| analytics.call('Item removed', event.payload) end

    event_store.subscribe(Orders::Events::CoppyItemAdded) do |event| analytics.call(‘Copy of item added’, event.payload) end event_store.subscribe(Orders::Events::ItemRemoved) do |event| analytics.call(‘Item removed', event.payload) end
  22. module Web::Controllers::CartItem class Create include Web::Action def call(params) halt(400) unless

    params.valid? events = event_store.get_stream(params[:order_id]) items = @project.call(Order::Projections::ItemList, events, {}) if items.include?(params[:item]) event_store.apply(Orders::Events::ItemAdded.new(payload)) else event_store.apply(Orders::Events::CoppyItemAdded.new(payload)) end redirect_to routes.order_path(params[:order_id]) end end end
  23. – Martin Fowler “You can use a different model to

    update information than the model you use to read information”
  24. DDD