Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Facing the Monolith: Overcoming Monolithic Appl...

Facing the Monolith: Overcoming Monolithic Applications with SOA

For many small sites with a minimal amount of complexity, a single rails application works fine. The problem is that as the application's complexity and scope grow, several problems arise. These include: heavy coupling, increased load and response times, and test complexity. All of these cause your feature development to slow considerably.

The concepts to solving these problems is relatively simple. The primary approach is extracting each concern in your application into its own service. The trick is extricating the data and classes when they are interdepend on the other aspects of the application.

We'll go over how this is done and what it meant when we had successfully teased the application apart. This approach was used on an application that collected, stored and managed millions of leads each month.

Charles Max Wood

October 09, 2012
Tweet

Other Decks in Technology

Transcript

  1. Charles Max Wood ‣ Lead Developer at Intentional Excellence Productions

    ‣ Host of Ruby Rogues, Javascript Jabber, and Ruby Freelancers podcasts Tuesday, October 9, 12
  2. HTTP lead_path = 'http://leads.myapp.com/leads/1.json' response = HTTParty.get(lead_path) #... Send lead

    to a school ... data = {lead_id: 1, school_id: 3, sent_at: ‘2012-10-2’} reporting_path = 'http://reporting.myapp.com/lead_sent/' HTTParty.post(reporting_path, {body: data}) Tuesday, October 9, 12
  3. HTTP lead_path = 'http://leads.myapp.com/leads/1.json' response = HTTParty.get(lead_path) #... Send lead

    to a school ... data = {lead_id: 1, school_id: 3, sent_at: ‘2012-10-2’} reporting_path = 'http://reporting.myapp.com/lead_sent/' HTTParty.post(reporting_path, {body: data}) Tuesday, October 9, 12
  4. HTTP lead_path = 'http://leads.myapp.com/leads/1.json' response = HTTParty.get(lead_path) #... Send lead

    to a school ... data = {lead_id: 1, school_id: 3, sent_at: ‘2012-10-2’} reporting_path = 'http://reporting.myapp.com/lead_sent/' HTTParty.post(reporting_path, {body: data}) Tuesday, October 9, 12
  5. HTTP lead_path = 'http://leads.myapp.com/leads/1.json' response = HTTParty.get(lead_path) #... Send lead

    to a school ... data = {lead_id: 1, school_id: 3, sent_at: ‘2012-10-2’} reporting_path = 'http://reporting.myapp.com/lead_sent/' HTTParty.post(reporting_path, {body: data}) Tuesday, October 9, 12
  6. HTTP ‣ Synchronous ‣ Response Needed to Continue ‣ Immediate

    Action ‣ Information Requests Tuesday, October 9, 12
  7. Resque class LeadSubmitter @queue = :sell_leads def perform(lead_id, school_id =

    nil) lead = Lead.request(lead_id) school = School.find_by_id(school_id) if school_id school ||= SchoolFinder.find_school_for(lead) school.submit(lead) Resque.enqueue(Reporting, :lead_submitted, lead_id, school.id, Time.now) end end class Lead < ActiveRecord::Base after_create :sell_lead def sell_lead Resque.enqueue(LeadSubmitter, self.id, self.school_id) end end Tuesday, October 9, 12
  8. Resque class LeadSubmitter @queue = :sell_leads def perform(lead_id, school_id =

    nil) lead = Lead.request(lead_id) school = School.find_by_id(school_id) if school_id school ||= SchoolFinder.find_school_for(lead) school.submit(lead) Resque.enqueue(Reporting, :lead_submitted, lead_id, school.id, Time.now) end end class Lead < ActiveRecord::Base after_create :sell_lead def sell_lead Resque.enqueue(LeadSubmitter, self.id, self.school_id) end end Tuesday, October 9, 12
  9. Resque class LeadSubmitter @queue = :sell_leads def perform(lead_id, school_id =

    nil) lead = Lead.request(lead_id) school = School.find_by_id(school_id) if school_id school ||= SchoolFinder.find_school_for(lead) school.submit(lead) Resque.enqueue(Reporting, :lead_submitted, lead_id, school.id, Time.now) end end class Lead < ActiveRecord::Base after_create :sell_lead def sell_lead Resque.enqueue(LeadSubmitter, self.id, self.school_id) end end Tuesday, October 9, 12
  10. Resque class LeadSubmitter @queue = :sell_leads def perform(lead_id, school_id =

    nil) lead = Lead.request(lead_id) school = School.find_by_id(school_id) if school_id school ||= SchoolFinder.find_school_for(lead) school.submit(lead) Resque.enqueue(Reporting, :lead_submitted, lead_id, school.id, Time.now) end end class Lead < ActiveRecord::Base after_create :sell_lead def sell_lead Resque.enqueue(LeadSubmitter, self.id, self.school_id) end end Tuesday, October 9, 12
  11. Resque class LeadSubmitter @queue = :sell_leads def perform(lead_id, school_id =

    nil) lead = Lead.request(lead_id) school = School.find_by_id(school_id) if school_id school ||= SchoolFinder.find_school_for(lead) school.submit(lead) Resque.enqueue(Reporting, :lead_submitted, lead_id, school.id, Time.now) end end class Lead < ActiveRecord::Base after_create :sell_lead def sell_lead Resque.enqueue(LeadSubmitter, self.id, self.school_id) end end Tuesday, October 9, 12
  12. Resque class LeadSubmitter @queue = :sell_leads def perform(lead_id, school_id =

    nil) lead = Lead.request(lead_id) school = School.find_by_id(school_id) if school_id school ||= SchoolFinder.find_school_for(lead) school.submit(lead) Resque.enqueue(Reporting, :lead_submitted, lead_id, school.id, Time.now) end end class Lead < ActiveRecord::Base after_create :sell_lead def sell_lead Resque.enqueue(LeadSubmitter, self.id, self.school_id) end end Tuesday, October 9, 12
  13. Queuing ‣ Asynchronous ‣ Response Not Needed to Continue ‣

    Writing New Data ‣ Jobs Done Out of Band Tuesday, October 9, 12
  14. JSON Feed class LeadController < ApplicationController def feed marker =

    params[:marker] leads = Lead.where(['id > ?', marker.to_i]).all if marker leads ||= Lead.all render json: leads end end # daemon require 'lead' while(true) do marker = 0 response = HTTParty.get("http://leads.myapp.com/feed?marker=#{marker}") lead_hashes = response.parsed_response lead_hashes.each do |lead_hash| lead = Lead.new(lead_hash) Worker.process lead end marker = lead_hashes.last[:id] unless lead_hashes.empty? sleep(1) if lead_hashes.empty? end Tuesday, October 9, 12
  15. JSON Feed class LeadController < ApplicationController def feed marker =

    params[:marker] leads = Lead.where(['id > ?', marker.to_i]).all if marker leads ||= Lead.all render json: leads end end # daemon require 'lead' while(true) do marker = 0 response = HTTParty.get("http://leads.myapp.com/feed?marker=#{marker}") lead_hashes = response.parsed_response lead_hashes.each do |lead_hash| lead = Lead.new(lead_hash) Worker.process lead end marker = lead_hashes.last[:id] unless lead_hashes.empty? sleep(1) if lead_hashes.empty? end Tuesday, October 9, 12
  16. JSON Feed class LeadController < ApplicationController def feed marker =

    params[:marker] leads = Lead.where(['id > ?', marker.to_i]).all if marker leads ||= Lead.all render json: leads end end # daemon require 'lead' while(true) do marker = 0 response = HTTParty.get("http://leads.myapp.com/feed?marker=#{marker}") lead_hashes = response.parsed_response lead_hashes.each do |lead_hash| lead = Lead.new(lead_hash) Worker.process lead end marker = lead_hashes.last[:id] unless lead_hashes.empty? sleep(1) if lead_hashes.empty? end Tuesday, October 9, 12
  17. JSON Feed class LeadController < ApplicationController def feed marker =

    params[:marker] leads = Lead.where(['id > ?', marker.to_i]).all if marker leads ||= Lead.all render json: leads end end # daemon require 'lead' while(true) do marker = 0 response = HTTParty.get("http://leads.myapp.com/feed?marker=#{marker}") lead_hashes = response.parsed_response lead_hashes.each do |lead_hash| lead = Lead.new(lead_hash) Worker.process lead end marker = lead_hashes.last[:id] unless lead_hashes.empty? sleep(1) if lead_hashes.empty? end Tuesday, October 9, 12
  18. JSON Feed class LeadController < ApplicationController def feed marker =

    params[:marker] leads = Lead.where(['id > ?', marker.to_i]).all if marker leads ||= Lead.all render json: leads end end # daemon require 'lead' while(true) do marker = 0 response = HTTParty.get("http://leads.myapp.com/feed?marker=#{marker}") lead_hashes = response.parsed_response lead_hashes.each do |lead_hash| lead = Lead.new(lead_hash) Worker.process lead end marker = lead_hashes.last[:id] unless lead_hashes.empty? sleep(1) if lead_hashes.empty? end Tuesday, October 9, 12
  19. JSON Feed class LeadController < ApplicationController def feed marker =

    params[:marker] leads = Lead.where(['id > ?', marker.to_i]).all if marker leads ||= Lead.all render json: leads end end # daemon require 'lead' while(true) do marker = 0 response = HTTParty.get("http://leads.myapp.com/feed?marker=#{marker}") lead_hashes = response.parsed_response lead_hashes.each do |lead_hash| lead = Lead.new(lead_hash) Worker.process lead end marker = lead_hashes.last[:id] unless lead_hashes.empty? sleep(1) if lead_hashes.empty? end Tuesday, October 9, 12
  20. A Few Notes... lead_path = 'http://leads.myapp.com/leads/1.json' response = HTTParty.get(lead_path) #...

    Send lead to a school ... data = {lead_id: 1, school_id: 3, sent_at: ‘2012-10-2’} reporting_path = 'http://reporting.myapp.com/lead_sent/' HTTParty.post(reporting_path, {body: data}) Tuesday, October 9, 12
  21. A Few Notes... lead_path = 'http://leads.myapp.com/leads/1.json' response = HTTParty.get(lead_path) #...

    Send lead to a school ... data = {lead_id: 1, school_id: 3, sent_at: ‘2012-10-2’} reporting_path = 'http://reporting.myapp.com/lead_sent/' HTTParty.post(reporting_path, {body: data}) Tuesday, October 9, 12
  22. A Few Notes... class LeadController < ApplicationController def feed marker

    = params[:marker] leads = Lead.where(['id > ?', marker.to_i]).all if marker leads ||= Lead.all render json: leads end end # daemon require 'lead' while(true) do marker = 0 response = HTTParty.get("http://leads.myapp.com/feed?marker=#{marker}") lead_hashes = response.parsed_response lead_hashes.each do |lead_hash| lead = Lead.new(lead_hash) Worker.process lead end marker = lead_hashes.last[:id] unless lead_hashes.empty? sleep(1) if lead_hashes.empty? end Tuesday, October 9, 12
  23. Naming Services ‣ Domains ‣ Subdomains (DNS) ‣ Paths (rack-mount)

    ‣ Server Name ‣ IP Address Tuesday, October 9, 12
  24. Security ‣ SSL ‣ Encryption(Shared key) ‣ Server Registration ‣

    API keys ‣ IP Whitelist (hosts.allow) ‣ Firewalls ‣ Authentication/Authorization Tuesday, October 9, 12
  25. Charles Max Wood ‣ Lead Developer at Intentional Excellence Productions

    ‣ http://rubyrogues.com ‣ @cmaxw ‣ 801-367-6164 ‣ [email protected] Tuesday, October 9, 12