Events. Events. Events!

Events. Events. Events!

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

E7dfc74cd6e3194a22ef6fac2c51f0ae?s=128

Anton Davydov

March 24, 2019
Tweet

Transcript

  1. 1.
  2. 2.
  3. 4.
  4. 5.
  5. 8.
  6. 9.
  7. 11.
  8. 14.

    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
  9. 16.
  10. 18.
  11. 22.
  12. 23.

    • Software developer at Hippo • OpenSource evangelist • Hanami

    core developer • Try to make own product • Technical consulting
  13. 24.
  14. 25.

    • Coffee • Beer • Psychology • SW-2039-0208-4228 • How

    to draw images for presentations • Bad stories
  15. 26.

    • Coffee • Beer • Psychology • SW-2039-0208-4228 • How

    to draw images for presentations • Bad stories
  16. 29.
  17. 33.
  18. 34.
  19. 35.
  20. 36.
  21. 37.
  22. 38.
  23. 39.

    Git

  24. 41.
  25. 43.
  26. 45.
  27. 64.
  28. 65.
  29. 69.
  30. 70.
  31. 71.
  32. 73.
  33. 74.
  34. 75.
  35. 76.
  36. 77.
  37. 79.
  38. 80.
  39. 81.
  40. 82.
  41. 83.
  42. 95.
  43. 96.
  44. 97.
  45. 98.
  46. 99.
  47. 100.
  48. 101.
  49. 102.
  50. 103.
  51. 104.
  52. 105.
  53. 106.
  54. 112.
  55. 113.
  56. 115.

    Event stream: bus#1 Event stream: bus#1 Event stream: bus#2 Event

    stream: bus#1 Event stream: bus#2 Event stream: bus#2
  57. 124.
  58. 125.
  59. 126.
  60. 127.
  61. 128.
  62. 129.
  63. 131.
  64. 135.
  65. 144.

    ES -> event happened and I store it ED ->

    event happened and I say everyone it
  66. 146.
  67. 149.

    • You don’t work with tables you work with events

    and changes • Experemental data structures • Easy to change database implementations
  68. 150.
  69. 151.

    • Hard for understand idea and complicated abstraction • Not

    popular in ruby • Developers need deprogramming
  70. 152.

    • Harder to get state than CRUD • Hard to

    understand the whole chain of events • Another architecture type
 with different DB structure
  71. 156.
  72. 158.
  73. 159.
  74. 160.
  75. 161.
  76. 162.
  77. 163.
  78. 166.
  79. 167.
  80. 168.
  81. 169.
  82. 171.
  83. 172.
  84. 175.
  85. 178.
  86. 179.
  87. 180.
  88. 182.
  89. 184.
  90. 185.
  91. 187.
  92. 188.
  93. 189.
  94. 196.

    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
  95. 200.

    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
  96. 205.

    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
  97. 207.

    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
  98. 209.

    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
  99. 210.

    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
  100. 211.

    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
  101. 212.

    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
  102. 213.

    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
  103. 214.

    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
  104. 216.

    # 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
  105. 217.

    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
  106. 219.
  107. 221.
  108. 222.
  109. 223.
  110. 226.
  111. 227.
  112. 229.

    – Martin Fowler “You can use a different model to

    update information than the model you use to read information”
  113. 233.

    DDD

  114. 237.
  115. 238.
  116. 239.
  117. 241.