From ActiveRecord to EventSourcing

60 %
40 %
Information about From ActiveRecord to EventSourcing

Published on March 16, 2014

Author: emadb


From ActiveRecord to Events Emanuele DelBono @emadb

Customer Address Invoice Items Contacts Role Contract Price City

@emadb I’m a software developer based in Italy. I develop my apps in C#, Javascript and some Ruby. I’m a wannabe Ruby dev.

Lasagna architecture

View Controller Model (AR) Database


Active record “An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.” M. Fowler

Active record 1 table => 1 class Too coupled with the database structure No SRP Database first class User < ActiveRecord::Base attr_accessible :email, :password belongs_to :group end

Get vs Post def index @products = Product.all end ! def create @product = if redirect_to @product else render action: 'new' end end

Active Record A single model cannot be appropriate for reporting, searching and transactional behaviour. Greg Young

Read vs Write Reads and writes are two different concerns Reads are simpler Writes need logic


Command Query Responsibility Segregation

CQRS Presentation Layer Handler BL Repository Write DB Read DB Query Service Denormalizer

CQRS Fully normalized Reads are easy Writes became easier SELECT fields FROM table (WHERE …)

Object state id basket_id article_id quantity 1 4 8 1 2 3 8 3 3 3 6 1 4 4 5 1

Thinking in events Every change is an event. add_item 1 add_item 2 remove_item 1 add_item 3 time

Event Sourcing Capture all changes to an application state as a sequence of events. M.Fowler If the changes are stored in a database, we can rebuild the state re-applying the events.

Event Sourcing Presentation Layer Bus Handler DMRepository Event store Denormalizer Query service Read DB Command Events

Pros • Encapsulation • Separation of concern • Simple storage • Performance/Scaling • Simple testing • More information granularity • Easy integration with other services

Cons • Complex for simple scenarios • Cost of infrastructure • Long-living objects needs time to be reconstructed • Tools needed (i.e. rebuild the state)

Ingredients • Rails app (no ActiveRecord) • Redis (pub-sub) • Sequel for querying data • MongoDb (event store) • Sqlite (Read db) • Wisper (domain events)

Domain Model Basket BasketItem Article * 1 • Fully encapsulated (no accessors) • Fully OOP • Events for communication • PORO


include CommandExecutor ! def add_to_basket send_command {"basket_id" => 42, "article_id" => params[:id].to_i}) redirect_to products_url end Controller Bus Command POST /Products add_to_basket

module CommandExecutor ! def send_command (command) class_name = channel = class_name.sub(/Command/, '') @redis.publish channel, command.to_json end ! end send_command

def consume(data) basket = repository.get_basket(data["basket_id"]) article = repository.get_article(data["article_id"]) basket.add_item article ! basket.commit end Bus Handler Command handler

class Basket include AggregateRootHelper ! def add_item (item) raise_event :item_added, { basket_id: id, item_code: item.code, item_price: item.price } end # ... ! end add_item

def raise_event(event, args) @uncommited_events << {name: event, args: args} send "on_#{event}", args end raise_event DM (Basket) Events

def get_item (item_code){|i| i.item_code == item_code}.try :first end ! def on_item_added (item) get_item(item[:item_code]).try(:increase_quantity) || @items << end on_item_added DM (Basket) Events

def commit while event = uncommited_events.shift, event) send_event event end end commit DM (Basket) Event store

Event Store

def item_added(data) db = Sequel.sqlite(AppSettings.sql_connection) article = db[:products_view].where(code: data[:item_code]).first basket = db[:basket_view].where('basket_id = ? AND article_id = ?', data[:basket_id], article[:id].to_i).first if basket.nil? #insert else db[:basket_view].where(id: basket[:id]).update(quantity: (basket[:quantity] + 1)) end end denormalizer Denormalizer


def index @products=db[:basket_view] end Controller Read DB Query GET /Products index

Conclusion • Stop thinking in CRUD • Read and Write are different • Domain model should be based on PORO • CQRS/ES is useful in complex scenario • Ruby power helps a lot (less infrastructure code)

#insert presentations

Add a comment

Related pages

Activerecord | LinkedIn

View 3842 Activerecord posts, presentations, experts, and more. Get the professional knowledge you need on LinkedIn.
Read more

A Case for Event Sourcing in Browser JS Apps – Ross Tuck

In the first example, we’re using a pretty standard getter/setter, ActiveRecord model. In the latter, events are generated inside the JS entities (yes ...
Read more

fhwang/event_sourced_record · GitHub Event Sourced Record. Event Sourced Record offers an idiomatic way to use the Event Sourcing pattern in Rails code. With Event Sourcing, every ...
Read more

Building an Event Sourced application using rails_event ...

No matter why let's dive into building an Event Sourced application in Rails ... use EventSourcing and ... defined as a simple ActiveRecord ...
Read more

rest - ASP.Net MVC with web service as model? - Stack Overflow

Spring.Net also allows you to wrap service interfaces in their contain with a web service ... CQRS+EventSourcing ... or ActiveRecord / linq2sql ...
Read more

alexaitken/synapse · GitHub

alexaitken / synapse. Code Pull requests 0 Pulse Graphs A versatile CQRS and event sourcing framework for Ruby 233 commits 2 branches ...
Read more