Skip to content

Instantly share code, notes, and snippets.

@FranckyU
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FranckyU/9024250 to your computer and use it in GitHub Desktop.
Save FranckyU/9024250 to your computer and use it in GitHub Desktop.
Rails response to CQRS
# I have been recently exposed to Martin Fowler's CQRS concept and saw some implementations on .Net
# Like this http://www.codeproject.com/Articles/555855/Introduction-to-CQRS
# Disliked it as it brings a lot of noise in the code, a lot of handlers, class imbrications that defies simple human logic
# To say shortly, it's not quite cool, and as a rubyist I like keeping it simple and human
# Though the core concept of CQRS is quite simple, separate writes and reads to different database connections to increase global performance
# But implementation is very complex
# Lastly, someone I know even spent 2 years on a .Net project using CQRS (don't know how many weeks/months spend for just implementing the CQRS thing)
# I wrote this gist to see whether it can be done in 10 minutes
# And I did it in 10 minutes
# I love ActiveRecord so I DRYed by just extending it
# HEY This is just a concept
# TODO battle test it and improve it
# TODO make it as a gem
# TODO do the database sharding and replication stuffs to make your data consistent over different hosts
# NOTE This is open source so feel free to use, modify and share it if the concept interests you
# -----------------------------------
# PUT THE FOLLOWING IN AN INITIALIZER
# -----------------------------------
module SwitchableConnection
class ClassExtension
def with_connection(connexion_array, &block)
# randomly pick a connection
# acting as a load balancer
# connections are picked in a uniform way
connexion = connexion_list[rand(connexion_array.length)]
ActiveRecord::Base.establish_connection(connexion) unless ActiveRecord::Base.connection_config == connexion
yield
end
end
def with_connection(connexion_array, &block)
self.class.with_connection(connexion_array, block)
end
def self.included(klass)
klass.extend(ClassExtension)
end
end
# Monkey patching ActiveRecord
# TODO move these connections configs into the config/database.yaml instead and load them from instead
module ActiveRecord
class Base
CONNECTIONS = {
read: [
{host: "any_host", adapter:"postgresql", encoding:"unicode", database:"db_name", pool: 5, username: "username", password:"xxxxxx"},
{host: "any_host", adapter:"postgresql", encoding:"unicode", database:"db_name", pool: 5, username: "username", password:"xxxxxx"},
{host: "any_host", adapter:"postgresql", encoding:"unicode", database:"db_name", pool: 5, username: "username", password:"xxxxxx"}
],
write: [
{host: "any_host", adapter:"postgresql", encoding:"unicode", database:"db_name", pool: 5, username: "username", password:"xxxxxx"},
{host: "any_host", adapter:"postgresql", encoding:"unicode", database:"db_name", pool: 5, username: "username", password:"xxxxxx"},
{host: "any_host", adapter:"postgresql", encoding:"unicode", database:"db_name", pool: 5, username: "username", password:"xxxxxx"}
]}
CONNECTIONS.each do |mode, connexion_array|
define_method "with_#{mode.to_s}_connection" do |&block|
with_connection(connexion_array, block)
end
self.class.instance_eval do
define_method "with_#{mode.to_s}_connection" do |&block|
with_connection(connexion_array, block)
end
end
end
end
end
# -----------------------------------
# USAGE
# -----------------------------------
# In a model
class Customer < ActiveRecord::Base
include SwitchableConnection
def a_complex_operation_method
# ....
with_read_connection do
end
# ....
with_write_connection do
end
# ....
end
end
# In a controller
class AnyController < ApplicationController
def any_action
# ....
Customer.with_read_connection do
# any db reaching code ....
end
# ....
Product.with_write_connection do
# any db reaching code ....
end
# ....
end
end
@FranckyU
Copy link
Author

Here's the updated versions which is even lighter https://gist.github.com/FranckyU/9024949

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment