Last active
August 29, 2015 13:56
-
-
Save FranckyU/9024250 to your computer and use it in GitHub Desktop.
Rails response to CQRS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here's the updated versions which is even lighter https://gist.github.com/FranckyU/9024949