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

Events. Events. Events! - krk.rb

Events. Events. Events! - krk.rb

Basic conceptions of event sourcing and how to use it in ruby.

Talk from https://krk-rb.pl

Anton Davydov

May 14, 2019
Tweet

More Decks by Anton Davydov

Other Decks in Programming

Transcript

  1. • Architect/software developer • OpenSource evangelist • Hanami core developer

    • Writer/programming streamer • Technical consulting
  2. • Coffee • Beer • Psychology • SW-2039-0208-4228 / 3DS

    also welcome • How to draw images for presentations • Bad stories
  3. • Coffee • Beer • Psychology • SW-2039-0208-4228 / 3DS

    also welcome • How to draw images for presentations • Bad stories
  4. Git

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

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

    event happened and I notify everyone about it
  7. • You don’t work with tables you work with events

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

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

    understand the whole chain of events • Another architecture type
 with different DB structure
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  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 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
  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 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
  20. # 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
  21. 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
  22. – Martin Fowler “You can use a different model to

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