Upload
dirk-breuer
View
850
Download
2
Embed Size (px)
DESCRIPTION
The slides of my talk at Rails-Konferenz 2009 in Frankfurt/Offenbach about bringing together the ideas of SOA with Rails to handle complexity.
Citation preview
Rails goes SOARails-Konferenz 2009 – Frankfurt/Offenbach
Dirk Breuer – pkw.de
02. September 2009
View
Model(enthält Business Logik + Daten)
Controller (nur lesend)
View
Model(enthält Business Logik)
Controller
BusinessLogik
(nur lesend)
View
Model(Datenhaltung)
Controller(nur lesend)Business
Logikextract
Vorteile der Transformation
Entwickler erhalten Kontrolle über Source Code zurück
Bessere Verständlichkeit für die Fachseite
Grafik © by Martin Laksman, 2003
Vorteile der Transformation
Entwickler erhalten Kontrolle über Source Code zurück
Bessere Verständlichkeit für die Fachseite
Bessere Time-to-Market
Grafik © by Martin Laksman, 2003
Service-Orientierte Architekturen
View
Model(Datenhaltung)
Controller(nur lesend)Services
(enthalten Business Logik)
How To SOA?
Fahrzeuge Suchen
Fahrzeuge fürdie Startseite
anzeigen
Suchauftrag fürSuche anlegen
Benutzerauthentifizieren
Statistiken fürein Fahrzeug
anzeigen
Statistiken fürein Fahrzeug
anzeigen
StatistikenSchreiben
get_startpage_carsget_startpage_dealerget_startpage_teasersearch_carscreate_temporary_customercreate_temporary_carsave_temporary_customersave_temporary_carauthenticate_customeradd_image_to_cargenerate_captchaverify_captchaaccept_customerpublish_carnotify_customer_about_published_car
get_statistics_of_carget_number_of_cars_of_customerget_cars_of_customerget_number_of_car_imagesget_car_imagesdelete_cardelete_customerget_search_orders_of_customerdelete_search_ordersearch_cars_by_search_orderauthenticate_customerauthenticate_dealergenerate_passwordsend_new_password…
Vorteile von SOA
Services werden von Fachseite gefunden
Fachseite versteht IT besser
Fachseite versteht Komplexität besser
Fachliche Sicht sehr langlebig, dadurch Services sehr langlebig
Vorteile von SOA
Services werden von Fachseite gefunden
Fachseite versteht IT besser
Fachseite versteht Komplexität besser
Fachliche Sicht sehr langlebig, dadurch Services sehr langlebig
Langlebigkeit ...?
Gra
fik ©
by
Mar
tin L
aksm
an, 2
003
Langlebigkeit der Datenbank?
Änderung von Spalten
Änderung des CRM-Systems
Anbindung an SAP
Zusammenlegung von StartUps
Langlebigkeit der GUI?
Änderung der Anordnung von Elementen
Änderung von Dialogen
Änderung von Texten
Redesign der gesamten Plattform
Langlebigkeit von Services
Fachseite definiert Geschäftsprozesse
Fahrzeuge suchen
Benutzer authentifizieren
Statistiken schreiben
Änderungen auf dieser Ebene sehr viel unwahrscheinlicher
Und was genau ist ein Service Jetzt?!
Grafik © by Martin Laksman, 2003
Ein Service ist …
… technologie- und plattformunabhängig
… dynamische lokalisier- und ausführbar
… in sich abgeschlossen
… durch eine wohl-definierte und formale Schnittstelle beschrieben
… stateless
Dienstbeschreibungveröffentlichen
Dienstimplementierungbinden
Dienstbeschreibungfinden
Service Registry
Service Provider
Service Client
Basic SOA
Aber kosten diese SOA Lösungen von IBM & Co nicht
sehr viel Geld?
Grafik © by Martin Laksman, 2003
Ja ...
Aber kosten diese SOA Lösungen von IBM & Co nicht
sehr viel Geld?
Grafik © by Martin Laksman, 2003
Das können wir uns nicht leisten!
Ja ...
Aber kosten diese SOA Lösungen von IBM & Co nicht
sehr viel Geld?
Grafik © by Martin Laksman, 2003
SOA ist doch nur ein Konzept. Das können
wir auch selbst umsetzen.
Grafik © by Martin Laksman, 2003
SOA ist doch nur ein Konzept. Das können
wir auch selbst umsetzen.
Und zwar passendfür uns!
Grafik © by Martin Laksman, 2003
+ SOA
class SearchService
def self.search_cars(search_criteria) # Calling the Car class which is just # a ActiveRecord::Base subclass end
end
found_cars = SearchService.search_cars(some_criteria)
class SearchService
def self.search_cars(search_criteria) # Calling the Car class which is just # a ActiveRecord::Base subclass end
end
found_cars = SearchService.search_cars(some_criteria)
Aber moment, das sieht
class Car < ActiveRecord::Base
def self.search(criteria) find(:all, :conditition => criteria) end
end
found_cars = Car.search(params[:criteria])
class Car < ActiveRecord::Base
def self.search(criteria) find(:all, :conditition => criteria) end
end
found_cars = Car.search(params[:criteria])Aber ...
Echte Businesslogik ist in der Regel deutlich komplexer
Bisher: ActiveRecord::Base = Businesslogik + Daten
Jetzt: ActiveRecord::Base = Daten, Service = Businesslogik
Echte Businesslogik ist in der Regel deutlich komplexer
Bisher: ActiveRecord::Base = Businesslogik + Daten
Jetzt: ActiveRecord::Base = Daten, Service = Businesslogik
inkl. einfacher Validierung etc.
Verteilung
Statistic Service
Such Service
SuchauftragService
AuthentifizierungsService
RailsFrontend
Verteilung
Statistic Service
Such Service
SuchauftragService
AuthentifizierungsService
RailsFrontend
?
?
?
Verteilung
Statistic Service
Such Service
SuchauftragService
AuthentifizierungsService
RailsFrontend
?
?
?Thrift-RPC
Thrift-RPC?!?
Grafik © by Martin Laksman, 2003
Thrift-RPC
RPC-Framework von Facebook entwickelt
Mittlerweile Apache Incubator Projekt: http://incubator.apache.org/thrift/
Ziel: Verlässliche und effiziente Kommunikation verteilter Systemkomponenten
Thrift-Architektur
Transport
Protocol
Processor
Service-Implementierung
Thrift-Architektur
Transport
Protocol
Processor
Service-Implementierung
Thrift-Compiler
Thrift-Gem
Thrift ServiceDefinitionerstellen
Thrift ServiceDefinitionerstellen
Service Stubsgenerieren
Thrift ServiceDefinitionerstellen
Service Stubsgenerieren
Serviceimplementieren
Thrift ServiceDefinitionerstellen
Transport-Schicht
realisierenService Stubsgenerieren
Serviceimplementieren
Thrift ServiceDefinitionerstellen
Transport-Schicht
realisierenService Stubsgenerieren
Serviceimplementieren
Serviceansprechen
Thrift ServiceDefinitionerstellen
Transport-Schicht
realisierenService Stubsgenerieren
Serviceimplementieren
Serviceansprechen
Thrift Service-Definition
namespace rb UserService
enum UserStates { VERIFIED = 1, UNVERIFIED = 2, BLOCKED = 3}
struct User { 1: string firstname, 2: string lastname, 3: i32 age, 4: string email, 5: string username, 6: UserStates state}
exception AuthenticationError { 1: string message, 2: list<string> backtrace}
service AuthenticationService { User authenticate_user(1:string username, 2:string password)throws (1:AuthenticationError ae)}
Thrift Service-Definition
✓Namespacesnamespace rb UserService
enum UserStates { VERIFIED = 1, UNVERIFIED = 2, BLOCKED = 3}
struct User { 1: string firstname, 2: string lastname, 3: i32 age, 4: string email, 5: string username, 6: UserStates state}
exception AuthenticationError { 1: string message, 2: list<string> backtrace}
service AuthenticationService { User authenticate_user(1:string username, 2:string password)throws (1:AuthenticationError ae)}
Thrift Service-Definition
✓Namespaces
✓Enums
namespace rb UserService
enum UserStates { VERIFIED = 1, UNVERIFIED = 2, BLOCKED = 3}
struct User { 1: string firstname, 2: string lastname, 3: i32 age, 4: string email, 5: string username, 6: UserStates state}
exception AuthenticationError { 1: string message, 2: list<string> backtrace}
service AuthenticationService { User authenticate_user(1:string username, 2:string password)throws (1:AuthenticationError ae)}
Thrift Service-Definition
✓Namespaces
✓Enums
✓Primitive Datentypen
namespace rb UserService
enum UserStates { VERIFIED = 1, UNVERIFIED = 2, BLOCKED = 3}
struct User { 1: string firstname, 2: string lastname, 3: i32 age, 4: string email, 5: string username, 6: UserStates state}
exception AuthenticationError { 1: string message, 2: list<string> backtrace}
service AuthenticationService { User authenticate_user(1:string username, 2:string password)throws (1:AuthenticationError ae)}
Thrift Service-Definition
✓Namespaces
✓Enums
✓Primitive Datentypen
✓Komplexe Typen
namespace rb UserService
enum UserStates { VERIFIED = 1, UNVERIFIED = 2, BLOCKED = 3}
struct User { 1: string firstname, 2: string lastname, 3: i32 age, 4: string email, 5: string username, 6: UserStates state}
exception AuthenticationError { 1: string message, 2: list<string> backtrace}
service AuthenticationService { User authenticate_user(1:string username, 2:string password)throws (1:AuthenticationError ae)}
Thrift Service-Definition
✓Namespaces
✓Enums
✓Primitive Datentypen
✓Komplexe Typen
✓Exceptions
namespace rb UserService
enum UserStates { VERIFIED = 1, UNVERIFIED = 2, BLOCKED = 3}
struct User { 1: string firstname, 2: string lastname, 3: i32 age, 4: string email, 5: string username, 6: UserStates state}
exception AuthenticationError { 1: string message, 2: list<string> backtrace}
service AuthenticationService { User authenticate_user(1:string username, 2:string password)throws (1:AuthenticationError ae)}
Thrift Service-Definition
✓Namespaces
✓Enums
✓Primitive Datentypen
✓Komplexe Typen
✓Exceptions
✓Services
namespace rb UserService
enum UserStates { VERIFIED = 1, UNVERIFIED = 2, BLOCKED = 3}
struct User { 1: string firstname, 2: string lastname, 3: i32 age, 4: string email, 5: string username, 6: UserStates state}
exception AuthenticationError { 1: string message, 2: list<string> backtrace}
service AuthenticationService { User authenticate_user(1:string username, 2:string password)throws (1:AuthenticationError ae)}
Walkthrough: Statistik-Service
namespace rb Pkwde.StatisticsModule
struct Statistic { 1:string name, 2:i32 count}
service StatisticsService { list<Statistic> statistic_for(1:list<string> element_names), oneway void increment(1:list<string> elements)}
$> thrift --gen rb:rails -o ./app/thrift config/statistics_service.thrift
app/thrift`-- gen-rb `-- pkwde `-- statistics_module |-- statistics_service.rb |-- statistics_service_constants.rb `-- statistics_service_types.rb
class StatisticsHandler def statistic_for(element_names) Statistic.find_all_by_name(element_names).map do |statistic| Pkwde::StatisticsModule::Statistic.new(:name => statistic.name, :count => statistic.count) end end def increment(elements) elements.each do |element| Statistic.find_or_create_by_name(element).increment!(:count) end end end
require 'thrift/server/rack_middleware'
ActionController::Dispatcher.middleware.insert_before(Rails::Rack::Metal, Thrift::RackMiddleware, { :processor => Pkwde::StatisticsModule::StatisticsService::Processor.new(StatisticsHandler.new), :hook_path => "/statistics" })
Service Provider
StatisticsHandler.new
require 'thrift/server/rack_middleware'
ActionController::Dispatcher.middleware.insert_before(Rails::Rack::Metal, Thrift::RackMiddleware, { :processor => Pkwde::StatisticsModule::StatisticsService::Processor.new(StatisticsHandler.new), :hook_path => "/statistics" })
Service Provider
StatisticsHandler.new
require 'thrift/server/rack_middleware'
ActionController::Dispatcher.middleware.insert_before(Rails::Rack::Metal, Thrift::RackMiddleware, { :processor => Pkwde::StatisticsModule::StatisticsService::Processor.new(StatisticsHandler.new), :hook_path => "/statistics" })
statistics_client = ThriftClient.new(:url => "http://localhost:3000/statistics", :client_class => Pkwde::StatisticsModule::StatisticsService::Client)
Service Provider
Service Client
StatisticsHandler.new
require 'thrift/server/rack_middleware'
ActionController::Dispatcher.middleware.insert_before(Rails::Rack::Metal, Thrift::RackMiddleware, { :processor => Pkwde::StatisticsModule::StatisticsService::Processor.new(StatisticsHandler.new), :hook_path => "/statistics" })
statistics_client = ThriftClient.new(:url => "http://localhost:3000/statistics", :client_class => Pkwde::StatisticsModule::StatisticsService::Client)
Service Provider
Service Client
http://github.com/railsbros/thrift4rails
http://github.com/railsbros/thrift
StatisticsHandler.new
Was ist Eigentlich mit einer Lösung die mehr
Ruby ist …?
Grafik © by Martin Laksman, 2003
Hoth::Services.define do service :increment_statistics, :params => [[:element]], :returns => nil, :endpoint => :statistics_module service :statistic_for, :params => [[:element_name]], :returns => [:statistic], :endpoint => :statistics_module end
Sneak Preview
Service-Definition
Hoth::ServiceDeployment.define do service_module :statistics_module do env :test, { :endpoint => Hoth::Endpoint.new(:host => 'localhost', :port => 3000), :mongrel_servers => 2, :mongrel_start_port => 9001 }
path "services/search_service" endend
Sneak Preview
Deployment-Definition
ENV["LOCAL"] = "false"
require 'hoth'require 'deployment_definition'require 'service_definition'
Hoth::Services.increment_statistics([statistic_object], event)Hoth::Services.statistic_of_cars([23]).inspect
Sneak Preview
Service-Client
ENV["LOCAL"] = "true"
require 'hoth'require 'deployment_definition'require 'service_definition'
class IncrementStatisticsImpl def self.execute(elements) elements.each do |element| Statistic.find_or_create_by_name(element).increment!(:count) end endend
class StatisticForImpl def self.execute(element_names) Statistic.find_all_by_name(element_names) endend
app = lambda {|env| [200, {'Content-Type' => 'text/plain'}, ""]}run Hoth::ServiceProvider.new(app)
Service-Provider
About Me …
Rubyist und Rails-Enthusiast seit Ende 2005
Software-Entwickler bei pkw.de seit 2007
Twitter: @railsbros_dirk
GitHub: http://github.com/pkwde
Blog: http://railsbros.de
Kann ich das gleiche nicht durch ein
gutes OO-Design realisieren?
Grafik © by Martin Laksman, 2003
SOA vs. OOP
SOA ersetzt OOP nicht!
SOA setzt auf einer anderen Ebene an
OOP Design Prinzipien finden jedoch keine Anwendung für SOA
Fokus auf die Abbildung von Geschäftsprozessen
Ein Service kann intern immer noch OOP sein