9

Subscribing for events in railseventstore

 3 years ago
source link: https://blog.arkency.com/2015/06/subscribing-for-events-in-rails-event-store/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client


hitbythebus.jpg?w=768&h=758&fit=max

Sample CQRS / ES application gone wrong

In my post Building an Event Sourced application I’ve included sample code to setup denormalizers (event handlers) that will build a read model:

def event_store
  @event_store ||= RailsEventStore::Client.new.tap do |es|
    es.subscribe(Denormalizers::Router.new)
  end
end

One router to rule them all

Because that is only a sample application showing how easy is to build an Event Sourced application using Ruby/Rails and Rails Event Store there were some shortcuts. Shortcuts that should have never been there. Shortcuts that have made some doubts for others who try to build their own solution.

The router was defined as:

module Denormalizers
  class Router
    def handle_event(event)
      case event.event_type
      when Events::OrderCreated.name      then Denormalizers::Order.new.order_created(event)
      when Events::OrderExpired.name      then Denormalizers::Order.new.order_created(event)
      when Events::ItemAddedToBasket      then Denormalizers::OrderLine.new.item_added_to_basket(event)
      when Events::ItemRemovedFromBasket  then Denormalizers::OrderLine.new.item_removed_from_basket(event)
      end
    end
  end
end

And denormalisers were implemented as:

module Denormalizers
  class Order
    def order_created(event)
      # ...
    end

    def order_expired(event)
      # ...
    end
  end
end

But we could remove it completely and we do not need that case at all!

All this code could be rewritten using rails_event_store subscriptions as follows:

#command handler (or anywhere you want to initialise rails_event_store
def event_store
  @event_store ||= RailsEventStore::Client.new.tap do |es|
    es.subscribe(Denormalizers::OrderCreated.new, ['Events::OrderCreated'])
    es.subscribe(Denormalizers::OrderExpired.new, ['Events::OrderExpired'])
    es.subscribe(Denormalizers::ItemAddedToBasket.new, ['Events::ItemAddedToBasket'])
    es.subscribe(Denormalizers::ItemRemovedFromBasket.new, ['Events::ItemRemovedFromBasket'])
  end
end

#sample event handler (denormaliser)
module Denormalizers
  class OrderCreated
    def handle_event(event)
      # ... denormalisation code here
    end
  end
end

You see? No Router at all! It’s event store who “knows” where to send messages (events) based on subscriptions defined.

Implicit assumptions a.k.a conventions

Sometimes when you have a simple application like this it is tempting to define “convention” and avoid the tedious need to setup all subscriptions. It seems to be easy to implement and (at least at the beginning of the project) it seems to be elegant and simple solution that would do “the magic” for us.

# WARNING: not recommended code ahead ;)
def event_store
  @event_store ||= RailsEventStore::Client.new.tap do |es|
    get_all_events_defined.each |event_class|
      handlers_for(event_class).each |handler|
        es.subscribe(handler, [event_class.to_s])
      end
    end
  end
end

def get_all_events_defined
  [ Events::OrderCreate, Events::OrderExpired, Events::ItemAddedToBasket, Events::ItemRemovedFromBasket ]
  # or implement some more sophisticated way of getting all event's classes ;)
end

def handlers_for(event_class)
  handler_class = "Denormalizers::#{event_class.name.demodulize}".constantize
  handler_class.new
end

I wonder what would happen if we called it “Implicit Assumptions” instead of “Convention over Configuration”.

— Andrzej Krzywda (@andrzejkrzywda) June 7, 2015

Naming is important! If we do not use convention but instead implicit assumption we will realise that it is not that simple and elegant at it looks like. Even worse, project tent to grow. When you will start using domain events you will want more and more of them. You could even want to have several handles for a single event ;) And maybe your handlers will need some dependencies? … Here is the moment when your simple convention breaks!

Make implicit explicit!

By coding the subscriptions one by one, maybe grouping them in some functional areas (bounded context) and clearly defining dependencies you could have more clear code, less “magic” and it should be easier to reason how things work.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK