Build and maintain large ruby applications - LA Ruby Oct meetup
-
Upload
enrico-teotti -
Category
Engineering
-
view
116 -
download
0
Transcript of Build and maintain large ruby applications - LA Ruby Oct meetup
build and maintain large Ruby applications
Enrico Teotti - @agenteo - http://teotti.com [email protected]
starting shortly
build and maintain large Ruby applications
Enrico Teotti - @agenteo - http://teotti.com
http://www.slideshare.net/agenteo/build-and-maintain-large-ruby-applications-ruby-conf-australia-2016
piadina
the curse of knowledge
yeasthoney salt
milk flourwaterlard
sugar
arugula
squacqueroneprosciutto
6 months later
– The law of continuing change (1974) Lehman, M
“Any software system used in the real-world must change or become less and less useful in that environment.”
– The law of increasing complexity (1974) Lehman, M
“As a program evolves, it becomes more complex, and extra resources are needed to preserve and simplify its structure.”
biscuits
mozzarella
sunflower oil
carrots
eggs
tomato puree
basil
mascarpone
coffee
cacao
yeasthoney salt
milk flourwaterlard
sugar
arugula
squacqueroneprosciutto
oregano
sunflower oil
carrots
eggs
tomato puree
basil
mascarpone
coffee
cacao
yeasthoney salt
milk flourwaterlard
sugar
arugula
squacqueroneprosciutto
oregano
piadina
pizza margherita
tiramisu
carrot cake
white ingredients
green ingredients
red ingredientsyellowish ingredients
orange ingredients
dark ingredients
white ingredientsgreen ingredients
classes grouped by design pattern
ls -l app/ controllers helpers models presenters services serializers strategies utils views
http://teotti.com/application-directories-named-as-architectural-patterns-antipattern/
# lib/blog/after_publish.rbmodule Blog class AfterPublish private def subscribe_blogger_to_promotion Promotions::Submission.new end endend
# lib/promotions/new_member.rbmodule Promotions class Submission private def fetch_member(id) # lib/membership/finder.rb Membership::Finder.new(id) end endend
promotionsblog membership
namespacescontext context
# lib/blog/after_publish.rbmodule Blog class AfterPublish private def subscribe_blogger_to_promotion Promotions::Submission.new end endend
# lib/promotions/new_member.rbmodule Promotions class Submission private def fetch_member(id) # lib/membership/finder.rb Membership::Finder.new(id) end endend
promotionsblog membership
namespacescontext context
promotions room decorator
name finderblog membershiprecipes
main Ruby application
comments
lib
3 years
promotions room decorator
name finderblog membershiprecipes
main Ruby application
comments
lib
3 years
Apiadina gem
pizza gem
C
shared ingredients gem
B
main Ruby application
spec.add_dependency "shared_ingredients"
# local_gems/pizza/pizza.gemspec
Apiadina gem
pizza gem
C
shared ingredients gem
B
main Ruby application
spec.add_dependency "shared_ingredients"
# local_gems/piadina/piadina.gemspec
piadina gem
pizza gem
C
shared ingredients gem
B
main Ruby application
desserts gem
DA
E
calzone gem
pizza dough gem
F
Conway’s Law“organizations which design systems … are constrained to produce designs which
are copies of the communication structures of these organizations"
piadina gem
pizza gem
shared ingredients gem
main Ruby application
desserts gem
DA
calzone gem
pizza dough gem
F
B
E
C
A
C
D
B
E
your health plan
drug information
claims platform
product information
membership
gem
gem
gem
gem
gem
dependency
main Ruby application
Sinatra / Rails / Hanami
http://teotti.com/create-dependency-structures-with-local-ruby-gems/
!"" Gemfile!"" Gemfile.lock!"" local_gems!"" run.rb#"" spec
ruby script that triggers entry point
gem’s behaviour
require 'health_plan'
subscriber_id = 'ASE123456789'aggregated_drug_information = HealthPlan::Aggregator.new(subscriber_id)puts aggregated_drug_information.details
main Ruby application
!"" Gemfile!"" Gemfile.lock!"" local_gems!"" run.rb#"" spec
path 'local_gems' do gem 'health_plan'end
source 'https://rubygems.org'
group :test do gem 'rspec'end
bundler’s Gemfile uses a path directive to find
local gems
main Ruby application
!"" Gemfile!"" Gemfile.lock!"" local_gems!"" run.rb#"" spec
bundler’s Gemfile uses a path directive to find
local gems
path 'local_gems' do gem 'health_plan'end
source 'https://rubygems.org'
group :test do gem 'rspec'end
main Ruby application
!"" Gemfile!"" Gemfile.lock!"" local_gems!"" run.rb#"" spec
directory where your local gems are
$ cd local_gems$ bundle gem health_plan create health_plan/Gemfile create health_plan/Rakefile create health_plan/LICENSE.txt create health_plan/README.md create health_plan/.gitignore create health_plan/health_plan.gemspec create health_plan/lib/health_plan.rb create health_plan/lib/health_plan/version.rbInitializing git repo in /Users/me/code/lab/gem-dependency-structure/local_gems/health_plan$ rm -Rf health_plan/.git*
bundle gem can create gems
main Ruby application
# local_gems/health_plan/health_plan.gemspeclib = File.expand_path('../lib', __FILE__)$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)require 'health_plan/version'
Gem::Specification.new do |spec| spec.name = "health_plan" spec.version = HealthPlan::VERSION spec.authors = ["Enrico Teotti"] spec.email = ["[email protected]"] spec.summary = %q{Write a short summary. Required.} spec.description = %q{Write a longer description. Optional.} spec.homepage = "" spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0") spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 1.7" spec.add_development_dependency "rake", "~> 10.0"
!"" Gemfile!"" Gemfile.lock!"" local_gems$ #"" health_plan!"" run.rb#"" spec
spec.add_development_dependency "rspec", "3.4.0"end
your health plan
# local_gems/health_plan/health_plan.gemspeclib = File.expand_path('../lib', __FILE__)$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)require 'health_plan/version'
Gem::Specification.new do |spec| spec.name = "health_plan" spec.version = HealthPlan::VERSION spec.authors = ["Enrico Teotti"] spec.email = ["[email protected]"] spec.summary = %q{Write a short summary. Required.} spec.description = %q{Write a longer description. Optional.} spec.homepage = "" spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0") spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 1.7" spec.add_development_dependency "rake", "~> 10.0"
!"" Gemfile!"" Gemfile.lock!"" local_gems$ #"" health_plan!"" run.rb#"" spec
spec.add_development_dependency "rspec", "3.4.0"end
your health plan
# local_gems/health_plan/spec/health_plan/aggregator_spec.rbrequire 'spec_helper'
describe HealthPlan::Aggregator do
describe "#details" do it "should not throw exceptions" do aggregator = HealthPlan::Aggregator.new(12345) expect(aggregator.details).to eq({ name: 'The full package plan'}) end end
end
your health plan
!"" Gemfile!"" Gemfile.lock!"" local_gems$ #"" health_plan!"" run.rb#"" spec
# local_gems/health_plan/lib/health_plan/aggregator.rbmodule HealthPlan
class Aggregator def initialize(id) @subscriber_id = id end
def details { name: 'The full package plan'} end endend
# local_gems/health_plan/lib/health_plan.rbrequire "health_plan/version"require "health_plan/aggregator"
module HealthPlanend
gem entry point
your health plan
!"" Gemfile!"" Gemfile.lock!"" local_gems$ #"" health_plan!"" run.rb#"" spec
!"" Gemfile!"" Gemfile.lock!"" local_gems$ !"" drug_information$ #"" health_plan!"" run.rb#"" spec
your health plan
drug information
main Ruby application
$ cd local_gems$ bundle gem drug_information create drug_information/Gemfile create drug_information/Rakefile create drug_information/LICENSE.txt create drug_information/README.md create drug_information/.gitignore create drug_information/drug_information.gemspec create drug_information/lib/drug_information.rb create drug_information/lib/drug_information/version.rb
drug information
# local_gems/health_plan/spec/health_plan/aggregator_spec.rbrequire 'spec_helper'
describe HealthPlan::Aggregator do
describe "#details" do let(:fetched_drugs) { 'something' } before do fetcher_double = double('DrugInformation::Fetcher', details: fetched_drugs) allow(DrugInformation::Fetcher).to receive(:new).and_return(fetcher_double) end
it "should not throw exceptions" do aggregator = HealthPlan::Aggregator.new(12345) expect(aggregator.details).to eq({ name: 'The full package plan’,
drugs: fetched_drugs }) end end
end
your health plan
!"" Gemfile!"" Gemfile.lock!"" local_gems$ !"" drug_information$ #"" health_plan!"" run.rb#"" spec
your health plan
lib = File.expand_path('../lib', __FILE__)$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)require 'health_plan/version'
Gem::Specification.new do |spec| spec.name = "health_plan" spec.version = HealthPlan::VERSION spec.authors = ["Enrico Teotti"] spec.email = ["[email protected]"] spec.summary = %q{Write a short summary. Required.} spec.description = %q{Write a longer description. Optional.} spec.homepage = "" spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0") spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 1.7" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "3.4.0"
spec.add_dependency "drug_information"end
# local_gems/health_plan/health_plan.gemspec
!"" Gemfile!"" Gemfile.lock!"" local_gems$ !"" drug_information$ #"" health_plan!"" run.rb#"" spec
your health plan
# local_gems/health_plan/Gemfile!"" Gemfile!"" Gemfile.lock!"" local_gems$ !"" drug_information$ #"" health_plan!"" run.rb#"" spec
path '..'
source 'https://rubygems.org'
‘..’ represents the parent directory
# local_gems/health_plan/lib/health_plan/aggregator.rbmodule HealthPlan
class Aggregator def initialize(id) @subscriber_id = id end
def details fetched_drug_info = DrugInformation::Fetcher.new(@subscriber_id) { name: 'The full package plan', drugs: fetched_drug_info.details } end endend
# local_gems/health_plan/lib/health_plan.rbrequire "health_plan/version"require "health_plan/aggregator"
require "drug_information"
module HealthPlanend
gem entry point
your health plan
!"" Gemfile!"" Gemfile.lock!"" local_gems$ !"" drug_information$ #"" health_plan!"" run.rb#"" spec
require dependent gem
http://teotti.com/create-dependency-structures-with-local-ruby-gems/
A
C
D
B
E
main Ruby application
unit tested
unit tested
unit testedunit tested
unit tested
acceptance tests
A
C
main Ruby application
B
loaded in memory, deamon or webserver
unit tested
unit tested
not unit tested
http://teotti.com/create-dependency-structures-with-local-ruby-gems#gotcha-flaky-bugs-caused-by-missing-requirement-statements
A
C
D
B
E
main Ruby application
F
H
I L
membership payment API
payment platform
bank transaction
credit card transaction
your health plan API
drug information
claims platform
product information
membership
A
C
D
B
E
main Ruby application
F
H
I L
membership payment API
payment platform
bank transaction
credit card transaction
your health plan API
drug information
claims platform
product information
membership
A
C
D
B
E
main Ruby application
F
H
I L
membership payment API
payment platform
bank transaction
credit card transaction
your health plan API
drug information
claims platform
product information
membership
A
C
D
B
E
main Ruby application
F
H
I L
membership payment API
payment platform
bank transaction
credit card transaction
your health plan API
drug information
claims platform
product information
membership
A
C
D
B
E
main Ruby application
F
H
I L
membership payment API
payment platform
bank transaction
credit card transaction
your health plan API
drug information
claims platform
product information
membership
A
C
D
B
E
main Ruby application
F
H
I L
I find your use of Gems disturbing
Do I really look like a guy with a plan?
*nods then deletes your
Gem*
http://teotti.com/rails-service-oriented-architecture-alternative-with-components/
to be continued…
premature use of SOA
A
C
D
B
E
main Ruby application
F
H
I L
membership payment API
payment platform
bank transaction
credit card transaction
your health plan API
drug information
claims platform
product information
membership
main Ruby application
your health plan API
drug information
claims platform
product information membership
membership payment
APIpayment platform
bank transaction
credit card transaction
deploy parts of a monolith
http://teotti.com/deploy-parts-of-a-ruby-on-rails-application/
editorial admin ui
persistence
site search
componentized Ruby application
public content ui
shared ui
DB
deploy@publicServer $ RUNNING_MODE=public puma
http://teotti.com/deploy-parts-of-a-ruby-on-rails-application/
editorial admin ui
persistence
site search
Rails application
public content ui
shared ui
DB
legacy migration
AWS SQS
massage and transform content
legacy system
pull legacy content