SlideShare uma empresa Scribd logo
1 de 83
Baixar para ler offline
Service-Oriented
Design and Implement
     with Rails 3
     ihower @ Ruby Tuesday
          2010/12/15
About Me
•              a.k.a. ihower
    •   http://ihower.tw

    •   http://twitter.com/ihower

• Rails Developer since 2006
• The Organizer of Ruby Taiwan Community
 • http://ruby.tw
 • http://rubyconf.tw
Agenda
•   What’s SOA
•   Why SOA
•   Considerations
•   The tool set overview
•   Service side implement
•   Client side implement
•   Library packaging
•   Caching
What’s SOA
           Service oriented architectures



• “monolithic” approach is not enough
• SOA is a way to design complex applications
  by splitting out major components into
  individual services and communicating via
  APIs.
• a service is a vertical slice of functionality:
  database, application code and caching layer
a monolithic web app example
                 request




             Load
            Balancer




            WebApps




            Database
a SOA example
                                     request




                                 Load
       request
                                Balancer



     WebApp                  WebApps
for Administration           for User




       Services A    Services B




        Database     Database
Why SOA? Isolation
• Shared Resources
• Encapsulation
• Scalability
• Interoperability
• Reuse
• Testability
• Reduce Local Complexity
Shared Resources
• Different front-web website use the same
  resource.
• SOA help you avoiding duplication databases
  and code.
• Why not only shared database?
 • code is not DRY                 WebApp
                              for Administration
                                                      WebApps
                                                      for User


 • caching will be problematic
                                               Database
Encapsulation
• you can change underly implementation in
  services without affect other parts of system
 • upgrade library
 • upgrade to Ruby 1.9
 • upgrade to Rails 3
• you can provide API versioning
Scalability1: Partitioned
     Data Provides
•   Database is the first bottleneck, a single DB server
    can not scale. SOA help you reduce database load
•   Anti-pattern: only split the database
    •   model relationship is broken                   WebApps



    •   referential integrity
    •   increase code complexity            Database
                                               A
                                                                 Database
                                                                    B


•   Myth: database replication can not help you speed
    and consistency
Scalability 2: Caching

• SOA help you design caching system easier
 • Cache data at the right place and expire
    at the right times
 • Cache logical model, not physical
 • You do not need cache view everywhere
Scalability 3: Efficient
• Different components have different task
  loading, SOA can scale by service.

                               WebApps



              Load
             Balancer                                 Load
                                                     Balancer




    Services A    Services A    Services B   Services B    Services B   Services B
Security

• Different services can be inside different
  firewall
  • You can only open public web and
    services, others are inside firewall.
Interoperability
• HTTP is the most common interface, SOA
  help you integrate them:
 • Multiple languages
 • Internal system e.g. Full-text searching engine
 • Legacy database, system
 • External vendors
Reuse

• Reuse across multiple applications
• Reuse for public APIs
• Example: Amazon Web Services (AWS)
Testability

• Isolate problem
• Mocking API calls
 • Reduce the time to run test suite
Reduce Local
         Complexity
• Team modularity along the same module
  splits as your software
• Understandability: The amount of code is
  minimized to a quantity understandable by
  a small team
• Source code control
Design considerations

• Partition into Separate Services
• API Design
• Which Protocol
How to partition into
 Separate Services
• Partitioning on Logical Function
• Partitioning on Read/Write Frequencies
• Partitioning on Minimizing Joins
• Partitioning on Iteration Speed
on Iteration Speed
• Which parts of the app have clear defined
  requirements and design?
• Identify the parts of the application which
  are unlikely to change.
• For example:
  The first version data storage is using
  MySQL, but may change to NoSQL in the
  future without affecting front-app.
on Logical Function

• Higher-level feature services
 • articles, photos, bookmarks...etc
• Low-level infrastructure services
 • a shared key-value store, queue system
On Read/Write
        Frequencies
• Ideally, a service will have to work only with
  a single data store
• High read and low write: the service should
  optimize a caching strategy.
• High write and low read: don’t bother with
  caching
On Join Frequency

• Minimize cross-service joins.
• But almost all data in an app is joined to
  something else.
  • How often particular joins occur? by read/
    write frequency and logical separation.
  • Replicate data across services
    (For example: a activity stream by using messaging)
API Design Guideline
• Send Everything you need
  •   Unlike OOP has lots of finely grained method calls


• Parallel HTTP requests
  •   for multiple service requests


• Send as Little as Possible
  •   Avoid expensive XML
Versioning
•   Be able run multiple versions in parallel:
    Clients have time to upgrade rather than having to
    upgrade both client and server in locks step.
•   Ideally, you won’t have to run multiple versions for
    very long
•   Two solutions:
    •   Including a Version in URIs
    •   Using Accept Headers for Versioning
        (disadvantage: HTTP caching)
Physical Models &
     Logical Models
• Physical models are mapped to database
  tables through ORM. (It’s 3NF)
• Logical models are mapped to your
  business problem. (External API use it)
• Logical models are mapped to physical
  models by you.
Logical Models
• Not relational or normalized
• Maintainability
  • can change with no change to data store
  • can stay the same while the data store
    changes
• Better fit for REST interfaces
• Better caching
Which Protocol?

• SOAP
• XML-RPC
• REST
RESTful Web services
• Rails way
• Easy to use and implement
• REST is about resources
 • URI
 • HTTP Verbs: GET/PUT/POST/DELETE
 • Representations: HTML, XML, JSON...etc
The tool set

• Web framework
• XML Parser
• JSON Parser
• HTTP Client
• Model library
Web framework

• Ruby on Rails, but we don’t need afull features.
  (Rails3 can be customized because it’s lot more
  modular. We will discuss it later)

• Sinatra: a lightweight framework
• Rack: a minimal Ruby webserver interface
  library
ActiveResource

• Mapping RESTful resources as models in a
  Rails application.
• Use XML by default
• But not useful in practice, why?
XML parser

• http://nokogiri.org/
• Nokogiri ( ) is an HTML, XML, SAX, and
  Reader parser. Among Nokogiri’s many
  features is the ability to search documents
  via XPath or CSS3 selectors.
JSON Parser

• http://github.com/brianmario/yajl-ruby/
• An extremely efficient streaming JSON
  parsing and encoding library. Ruby C
  bindings to Yajl
HTTP Client

• How to run requests in parallel?
 • Asynchronous I/O
   • Reactor pattern (EventMachine)
 • Multi-threading
   • JRuby
Typhoeus
        http://github.com/pauldix/typhoeus/



• A Ruby library with native C extensions to
  libcurl and libcurl-multi.
• Typhoeus runs HTTP requests in parallel
  while cleanly encapsulating handling logic
Typhoeus: Quick
                   example
response = Typhoeus::Request.get("http://www.pauldix.net")
response = Typhoeus::Request.head("http://www.pauldix.net")
response = Typhoeus::Request.put("http://localhost:3000/posts/1",
                                 :body => "whoo, a body")
response = Typhoeus::Request.post("http://localhost:3000/posts",
                  :params => {:title => "test post", :content => "this is my test"})
response = Typhoeus::Request.delete("http://localhost:3000/posts/1")
Hydra handles requests
 but not guaranteed to run in any particular order
      HYDRA = Typhoeus::HYDRA.new

      a = nil
      request1 = Typhoeus::Request.new("http://example1")
      request1.on_complete do |response|
          a = response.body
      end
      HYDRA.queue(request1)

      b = nil
      request2 = Typhoeus::Request.new("http://example1")
      request2.on_complete do |response|
          b = response.body
      end
      HYDRA.queue(request2)

      HYDRA.run # a, b are set from here
a asynchronous method
 def foo_asynchronously
     request = Typhoeus::Request.new( "http://example" )
     request.on_complete do |response|
        result_value = ActiveSupport::JSON.decode(response.body)
        # do something
        yield result_value
      end

       self.hydra.queue(request)
 end
Usage
result = nil
foo_asynchronously do |i|
    result = i
end

foo_asynchronously do |i|
    # Do something for i
end

HYDRA.run
# Now you can use result1 and result2
a synchronous method

  def foo
     result = nil
     foo_asynchronously { |i| result = i }
     self.hydra.run
     result
   end
Physical Models
        mapping to database directly




• ActiveRecord
• DataMapper
• MongoMapper, MongoId
Logical Models


• ActiveModel: an interface and modules can
  be integrated with ActionPack helpers.
• http://ihower.tw/blog/archives/4940
integrated with helper?

• For example:
 • link_to post_path(@post)
 • form_for @post
 • @post.errors
A basic model
class YourModel
    extend ActiveModel::Naming
    include ActiveModel::Conversion
    include ActiveModel::Validations

      def persisted?
          false
      end
end
without validations
 class YourModel
     extend ActiveModel::Naming
     include ActiveModel::Conversion

       def persisted?
           false
       end

       def valid?() true end

       def errors
           @errors ||= ActiveModel::Errors.new(self)
       end
 end
Many useful modules
•   MassAssignmentSecurity
•   Serialization
•   Callback
•   AttributeMethods
•   Dirty
•   Observing
•   Translation
Serializers
class Person

  include ActiveModel::Serializers::JSON
  include ActiveModel::Serializers::Xml

  attr_accessor :name

  def attributes
    @attributes ||= {'name' => 'nil'}
  end

end

person = Person.new
person.serializable_hash   #   =>   {"name"=>nil}
person.as_json             #   =>   {"name"=>nil}
person.to_json             #   =>   "{"name":null}"
person.to_xml              #   =>   "<?xml version="1.0" encoding="UTF-8"?>
n<serial-person...
Mass Assignment

class YourModel
    # ...
    def initialize(attributes = {})
        if attributes.present?
          attributes.each { |k, v| send("#{k}=", v) if respond_to?("#{k}=") }
        end
      end
end

YourModel.new( :a => 1, :b => 2, :c => 3 )
MassAssignmentSecurity
class YourModel
    # ...
    include ActiveModel::MassAssignmentSecurity

    attr_accessible :first_name, :last_name

    def initialize(attributes = {})
        if attributes.present?
          sanitize_for_mass_assignment(attributes).each { |k, v| send("#{k}=", v) if
respond_to?("#{k}=") }
        end
    end
end
Scenario we want to
     implement
• an Users web service, which provide basic
  CRUD functions.
• an web application with the Users client
  library
Service implement
Customized Rails3

• We don’t need some components.
• We can customize ActionController
• Building a fast, lightweight REST service
  with Rails 3
  http://pivotallabs.com/users/jdean/blog/articles/1419-building-a-fast-
  lightweight-rest-service-with-rails-3
# config/appliction.rb

%w(
  active_record
  action_controller
  action_mailer
).each do |framework|
  begin
    require "#{framework}/railtie"
  rescue LoadError
  end
end
# config/application.rb
[
  Rack::Sendfile,
  ActionDispatch::Flash,
  ActionDispatch::Session::CookieStore,
  ActionDispatch::Cookies,
  ActionDispatch::BestStandardsSupport,
  Rack::MethodOverride,
  ActionDispatch::ShowExceptions,
  ActionDispatch::Static,
  ActionDispatch::RemoteIp,
  ActionDispatch::ParamsParser,
  Rack::Lock,
  ActionDispatch::Head
].each do |klass|
  config.middleware.delete klass
end

# config/environments/production.rb
config.middleware.delete
ActiveRecord::ConnectionAdapters::ConnectionManagement
# /app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
class ApplicationController < ActionController::Metal
  include AbstractController::Logger
  include Rails.application.routes.url_helpers
  include ActionController::UrlFor
  include ActionController::Rendering
  include ActionController::Renderers::All
  include ActionController::MimeResponds

  if Rails.env.test?
    include ActionController::Testing
    # Rails 2.x compatibility
    include ActionController::Compatibility
  end

end




                       http://ihower.tw/blog/archives/4561
APIs design best practices (1)
 • Routing doesn't need Rails resources
   mechanism , but APIs design should follow
   RESTful.
   (This is because we don't have view in service and we don't need URL
   helpers. So use resources mechanism is too overkill)


 • RESTful APIs is stateless, each APIs should
   be independent. So, requests which have
   dependency relationship should be
   combined into one API request. (atomic)
APIs design best practices (2)
 • The best format in most case is JSON.
   ( one disadvantage is we can’t return binary data directly. )


 • Use Yajl as parser.
   # config/application.rb
   ActiveSupport::JSON.backend = "Yajl"



 • Don't convert data to JSON in Model, the
   converting process to JSON should be
   place in Controller.
APIs design best practices (3)
•   I suggest it shouldn't include_root_in_json
    # config/application.rb
    ActiveRecord::Base.include_root_in_json = false

•   Please notice “the key is JSON must be string”.
    whether you use symbol or string in Ruby, after JSON
    encode should all be string.
•   related key format should be xxx_id or xxx_ids for
    example:
    { "user_id" => 4, "product_ids" => [1,2,5] }.to_json


•   return user_uri field in addition to the user_id field if
    need
a return data example
  model.to_json and model.to_xml is easy to use, but not useful in practice.




# one record
{ :name => "a" }.to_json


# collection
{ :collection => [ { :name => "a" } , { :name =>
"b" } ], :total => 123 }.to_json


                        If you want to have pagination, you
                                need total number.
APIs design best practices (4)
 • except return collection, we can also
   provide Multi-Gets API. through params :
   ids. ex. /users?ids=2,5,11,23
 • client should sort ID first, so we can design
   cache mechanism much easier.
 • another topic need to concern is the URL
   length of GET. So this API can also use
   POST.
an error message
       return example
{ :message => "faild", :error_codes => [1,2,3],
  :errors => ["k1" => "v1", "k2" => "v2" ] }.to_json
APIs design best practices (5)

 • error_codes & errors is optional, you can
   define it if you need.
 • errors is used to put model's validation
   error : model.errors.to_json
HTTP status code
         We should return suitable HTTP status code



• 200 OK
• 201 Created ( add success)
• 202 Accepted ( receive success but not
  process yet, in queue now )
• 400 Bad Request ( ex. Model Validation
  Error or wrong parameters )
• 401 Unauthorized
class PeopleController < ApplicationController

  def index
    @people = Person.paginate(:per_page => params[:per_page] || 20, :page => params[:page])
    render :json => { :collection => @people, :total => @people.total_entries }.to_json
  end

  def show
    @person = Person.find( params[:id] )
    render :json => @person.to_json
  end

  def create
    @person = Person.new( :name => params[:name], :bio => params[:bio],
                          :user_id => params[:user_id] )

    @person.save!
    render :json => { :id => @person.id }.to_json, :status => 201
  end

  def update
    @person = user_Person.find( params[:id] )
    @person.attributes = { :name => params[:name], :bio => params[:bio],
                           :user_id => params[:user_id] }

    @person.save!
    render :status => 200, :text => "OK"
  end

  def destroy
    @person = Person.find( params[:id] )
    @person.destroy

    render :status => 200, :text => "OK"
  end

end
Client implement
Note
• No active_record, we get data from service
  through HTTP client (typhoeus)
• Model can include some ActiveModel,
  modules so we can develop more efficiently.
• This model is logical model, mapping to the
  data from API, not database table. It's
  different to service's physical model ( ORM-
  based)
# config/appliction.rb

%w(
  action_controller
  action_mailer
).each do |framework|
  begin
    require "#{framework}/railtie"
  rescue LoadError
  end
end
Setup a global Hydry
  # config/initializers/setup_hydra.rb
  HYDRA = Typhoeus::Hydra.new
An example you can
 inherited from (1)
  class LogicalModel

    extend ActiveModel::Naming
    include ActiveModel::Conversion
    include ActiveModel::Serializers::JSON
    include ActiveModel::Validations
    include ActiveModel::MassAssignmentSecurity

    self.include_root_in_json = false

    # continued...
  end
class LogicalModel                                                     An example you can
  # continued...                                                        inherited from (2)
  def self.attribute_keys=(keys)
    @attribute_keys = keys
    attr_accessor *keys
  end

  def self.attribute_keys
    @attribute_keys
  end

  class << self
    attr_accessor :host, :hydra
  end

  def persisted?
    !!self.id
  end

  def initialize(attributes={})
    self.attributes = attributes
  end

  def attributes
    self.class.attribute_keys.inject(ActiveSupport::HashWithIndifferentAccess.new) do |result, key|
      result[key] = read_attribute_for_validation(key)
      result
    end
  end

  def attributes=(attrs)
    sanitize_for_mass_assignment(attrs).each { |k, v| send("#{k}=", v) if respond_to?("#{k}=") }
  end
Model usage example

class Person < LogicalModel

  self.attribute_keys = [:id, :name, :bio, :user_id, :created_at, :updated_at]
  self.host = PEOPLE_SERVICE_HOST
  self.hydra = HYDRA

  validates_presence_of :title, :url, :user_id

  # ...
end
class Person < LogicalModel
  # ...
                                                                      paginate
  def self.people_uri
    "http://#{self.host}/apis/v1/people.json"
  end

  def self.async_paginate(options={})
    options[:page] ||= 1
    options[:per_page] ||= 20
    request = Typhoeus::Request.new(people_uri, :params => options)
    request.on_complete do |response|
      if response.code >= 200 && response.code < 400
        log_ok(response)

          result_set = self.from_json(response.body)
          collection = result_set[:collection].paginate( :total_entries => result_set[:total] )
          collection.current_page = options[:page]
          yield collection
        else
          log_failed(response)
        end
      end

    self.hydra.queue(request)
  end

  def self.paginate(options={})
    result = nil
    async_paginate(options) { |i| result = i }
    self.hydra.run
    result
  end

end
will_paginate hack!
      • in order to use will_paginate's helper, we
          must set current_page manually, so we hack
          this way:

# /config/initializers/hack_will_paginate.rb
# This is because our search result via HTTP API is an array and need be paginated.
# So we need assign current_page, unless it will be always 1.

module WillPaginate
  class Collection
    def current_page=(s)
      @current_page = s.to_i
    end
  end
end
from_json & logging
class LogicalModel
  # ...

  def self.from_json(json_string)
    parsed = ActiveSupport::JSON.decode(json_string)
    collection = parsed["collection"].map { |i| self.new(i) }
    return { :collection => collection, :total => parsed["total"].to_i }
  end

  def self.log_ok(response)
    Rails.logger.info("#{response.code} #{response.request.url} in #{response.time}s")
  end

  def self.log_failed(response)
    msg = "#{response.code} #{response.request.url} in #{response.time}s FAILED: #{ActiveSupport::JSON.decode
(response.body)["message"]}"
    Rails.logger.warn(msg)
  end

  def log_ok(response)
    self.class.log_ok(response)
  end

  def log_failed(response)
    self.class.log_failed(response)
  end

end
class Person < LogicalModel
  # ...
                                                                                find
  def self.person_uri(id)
    "http://#{self.host}/apis/v1/people/#{id}.json"
  end

  def self.async_find(id)

      request = Typhoeus::Request.new( person_uri(id) )
      request.on_complete do |response|
        if response.code >= 200 && response.code < 400
          log_ok(response)
          yield self.new.from_json(response.body)
        else
          log_failed(response)
        end
      end
                                                     This from_json is defined by
                                                    ActiveModel::Serializers::JSON
    self.hydra.queue(request)
  end

  def self.find(id)
    result = nil
    async_find(id) { |i| result = i }
    self.hydra.run
    result
  end

end
class Person < LogicalModel
                                              create&update
  # ...

  def create
    return false unless valid?

    response = Typhoeus::Request.post( self.class.people_uri, :params => self.attributes )
    if response.code == 201
      log_ok(response)
      self.id = ActiveSupport::JSON.decode(response.body)["id"]
      return self
    else
      log_failed(response)
      return nil
    end
  end

  def update(attributes)
    self.attributes = attributes

    return false unless valid?
    response = Typhoeus::Request.put( self.class.person_uri(id), :params => self.attributes )
    if response.code == 200
      log_ok(response)
      return self
    else
      log_failed(response)         Normally data writes do not
      return nil                    need to occur in parallel
    end
  end
end                                                                        Or write to a messaging
                                                                           system asynchronously
delete&destroy
class Person < LogicalModel
  # ...

  def self.delete(id)
    response = Typhoeus::Request.delete( self.person_uri(id) )
    if response.code == 200
      log_ok(response)
      return self
    else
      log_failed(response)
      return nil
    end
  end

  def destroy
    self.class.delete(self.id)
  end

end
Service client Library
       packaging
• Write users.gemspec file
• gem build users.gemspec
• distribution
 • http://rubygems.org
 • build your local gem server
• http://ihower.tw/blog/archives/4496
About caching

• Internally
 • Memcached
• Externally: HTTP Caching
 • Rack-Cache,Varnish, Squid
The End
References
•       Books&Articles:
    •     Service-Oriented Design with Ruby and Rails, Paul Dix (Addison Wesley)
    •     Enterprise Rails, Dan Chak (O’Reilly)
    •     RESTful Web Services, Richardson&Ruby (O’Reilly)
    •     RESTful WEb Services Cookbook, Allamaraju&Amundsen (O’Reilly)
•       Blog:
    •     Rails3: ActiveModel         http://ihower.tw/blog/archives/4940
    •     Rubygems                    http://ihower.tw/blog/archives/4496
    •     Rails3: Railtie   Plugins       http://ihower.tw/blog/archives/4873
•       Slides:
    •     Distributed Ruby and Rails
          http://ihower.tw/blog/archives/3589

Mais conteúdo relacionado

Mais procurados

CUST-2 New Client Configuration & Extension Points in Share
CUST-2 New Client Configuration & Extension Points in ShareCUST-2 New Client Configuration & Extension Points in Share
CUST-2 New Client Configuration & Extension Points in ShareAlfresco Software
 
 Active Storage - Modern File Storage? 
 Active Storage - Modern File Storage?  Active Storage - Modern File Storage? 
 Active Storage - Modern File Storage? Michael Yagudaev
 
Scaling with swagger
Scaling with swaggerScaling with swagger
Scaling with swaggerTony Tam
 
From Ruby on Rails to RubyMotion - Writing your First iOS App with RubyMotion
From Ruby on Rails to RubyMotion - Writing your First iOS App with RubyMotionFrom Ruby on Rails to RubyMotion - Writing your First iOS App with RubyMotion
From Ruby on Rails to RubyMotion - Writing your First iOS App with RubyMotionMichael Denomy
 
OSDC 2013 | Introduction into Chef by Andy Hawkins
OSDC 2013 | Introduction into Chef by Andy HawkinsOSDC 2013 | Introduction into Chef by Andy Hawkins
OSDC 2013 | Introduction into Chef by Andy HawkinsNETWAYS
 
Getting started with rails active storage wae
Getting started with rails active storage waeGetting started with rails active storage wae
Getting started with rails active storage waeBishal Khanal
 
An Intense Overview of the React Ecosystem
An Intense Overview of the React EcosystemAn Intense Overview of the React Ecosystem
An Intense Overview of the React EcosystemRami Sayar
 
Web Development using Ruby on Rails
Web Development using Ruby on RailsWeb Development using Ruby on Rails
Web Development using Ruby on RailsAvi Kedar
 
CUST-10 Customizing the Upload File(s) dialog in Alfresco Share
CUST-10 Customizing the Upload File(s) dialog in Alfresco ShareCUST-10 Customizing the Upload File(s) dialog in Alfresco Share
CUST-10 Customizing the Upload File(s) dialog in Alfresco ShareAlfresco Software
 
PLAT-8 Spring Web Scripts and Spring Surf
PLAT-8 Spring Web Scripts and Spring SurfPLAT-8 Spring Web Scripts and Spring Surf
PLAT-8 Spring Web Scripts and Spring SurfAlfresco Software
 
Extreme Web Performance for Mobile Devices - Velocity NY
Extreme Web Performance for Mobile Devices - Velocity NYExtreme Web Performance for Mobile Devices - Velocity NY
Extreme Web Performance for Mobile Devices - Velocity NYMaximiliano Firtman
 
Developing Complex WordPress Sites without Fear of Failure (with MVC)
Developing Complex WordPress Sites without Fear of Failure (with MVC)Developing Complex WordPress Sites without Fear of Failure (with MVC)
Developing Complex WordPress Sites without Fear of Failure (with MVC)Mike Schinkel
 
Riding the Edge with Ember.js
Riding the Edge with Ember.jsRiding the Edge with Ember.js
Riding the Edge with Ember.jsaortbals
 
Adobe AEM CQ5 - Developer Introduction
Adobe AEM CQ5 - Developer IntroductionAdobe AEM CQ5 - Developer Introduction
Adobe AEM CQ5 - Developer IntroductionYash Mody
 
Containerdays Intro to Habitat
Containerdays Intro to HabitatContainerdays Intro to Habitat
Containerdays Intro to HabitatMandi Walls
 

Mais procurados (20)

CUST-2 New Client Configuration & Extension Points in Share
CUST-2 New Client Configuration & Extension Points in ShareCUST-2 New Client Configuration & Extension Points in Share
CUST-2 New Client Configuration & Extension Points in Share
 
 Active Storage - Modern File Storage? 
 Active Storage - Modern File Storage?  Active Storage - Modern File Storage? 
 Active Storage - Modern File Storage? 
 
Scaling with swagger
Scaling with swaggerScaling with swagger
Scaling with swagger
 
From Ruby on Rails to RubyMotion - Writing your First iOS App with RubyMotion
From Ruby on Rails to RubyMotion - Writing your First iOS App with RubyMotionFrom Ruby on Rails to RubyMotion - Writing your First iOS App with RubyMotion
From Ruby on Rails to RubyMotion - Writing your First iOS App with RubyMotion
 
Apereo OAE - Bootcamp
Apereo OAE - BootcampApereo OAE - Bootcamp
Apereo OAE - Bootcamp
 
OSDC 2013 | Introduction into Chef by Andy Hawkins
OSDC 2013 | Introduction into Chef by Andy HawkinsOSDC 2013 | Introduction into Chef by Andy Hawkins
OSDC 2013 | Introduction into Chef by Andy Hawkins
 
Getting started with rails active storage wae
Getting started with rails active storage waeGetting started with rails active storage wae
Getting started with rails active storage wae
 
遇見 Ruby on Rails
遇見 Ruby on Rails遇見 Ruby on Rails
遇見 Ruby on Rails
 
An Intense Overview of the React Ecosystem
An Intense Overview of the React EcosystemAn Intense Overview of the React Ecosystem
An Intense Overview of the React Ecosystem
 
Web Development using Ruby on Rails
Web Development using Ruby on RailsWeb Development using Ruby on Rails
Web Development using Ruby on Rails
 
CUST-10 Customizing the Upload File(s) dialog in Alfresco Share
CUST-10 Customizing the Upload File(s) dialog in Alfresco ShareCUST-10 Customizing the Upload File(s) dialog in Alfresco Share
CUST-10 Customizing the Upload File(s) dialog in Alfresco Share
 
A Day of REST
A Day of RESTA Day of REST
A Day of REST
 
PLAT-8 Spring Web Scripts and Spring Surf
PLAT-8 Spring Web Scripts and Spring SurfPLAT-8 Spring Web Scripts and Spring Surf
PLAT-8 Spring Web Scripts and Spring Surf
 
Extreme Web Performance for Mobile Devices - Velocity NY
Extreme Web Performance for Mobile Devices - Velocity NYExtreme Web Performance for Mobile Devices - Velocity NY
Extreme Web Performance for Mobile Devices - Velocity NY
 
RESTful Services
RESTful ServicesRESTful Services
RESTful Services
 
WCM-7 Surfing with CMIS
WCM-7 Surfing with CMISWCM-7 Surfing with CMIS
WCM-7 Surfing with CMIS
 
Developing Complex WordPress Sites without Fear of Failure (with MVC)
Developing Complex WordPress Sites without Fear of Failure (with MVC)Developing Complex WordPress Sites without Fear of Failure (with MVC)
Developing Complex WordPress Sites without Fear of Failure (with MVC)
 
Riding the Edge with Ember.js
Riding the Edge with Ember.jsRiding the Edge with Ember.js
Riding the Edge with Ember.js
 
Adobe AEM CQ5 - Developer Introduction
Adobe AEM CQ5 - Developer IntroductionAdobe AEM CQ5 - Developer Introduction
Adobe AEM CQ5 - Developer Introduction
 
Containerdays Intro to Habitat
Containerdays Intro to HabitatContainerdays Intro to Habitat
Containerdays Intro to Habitat
 

Semelhante a Service-Oriented Design and Implement with Rails3

Service-oriented architecture
Service-oriented architectureService-oriented architecture
Service-oriented architectureShalva Usubov
 
Dropping ACID: Wrapping Your Mind Around NoSQL Databases
Dropping ACID: Wrapping Your Mind Around NoSQL DatabasesDropping ACID: Wrapping Your Mind Around NoSQL Databases
Dropping ACID: Wrapping Your Mind Around NoSQL DatabasesKyle Banerjee
 
Cross-platform interaction
Cross-platform interactionCross-platform interaction
Cross-platform interactionOleksii Duhno
 
Restful风格ž„web服务架构
Restful风格ž„web服务架构Restful风格ž„web服务架构
Restful风格ž„web服务架构Benjamin Tan
 
Alfresco Tech Talk Live (Episode 70): Customizing Alfresco Share 4.2
Alfresco Tech Talk Live (Episode 70): Customizing Alfresco Share 4.2Alfresco Tech Talk Live (Episode 70): Customizing Alfresco Share 4.2
Alfresco Tech Talk Live (Episode 70): Customizing Alfresco Share 4.2Richard Esplin
 
Lecture #5 Introduction to rails
Lecture #5 Introduction to railsLecture #5 Introduction to rails
Lecture #5 Introduction to railsEvgeniy Hinyuk
 
Introduction to Rails by Evgeniy Hinyuk
Introduction to Rails by Evgeniy HinyukIntroduction to Rails by Evgeniy Hinyuk
Introduction to Rails by Evgeniy HinyukPivorak MeetUp
 
Building Software Backend (Web API)
Building Software Backend (Web API)Building Software Backend (Web API)
Building Software Backend (Web API)Alexander Goida
 
Microservices and Best Practices
Microservices and Best Practices Microservices and Best Practices
Microservices and Best Practices Weaveworks
 
Rainbows, Unicorns, and other Fairy Tales in the Land of Serverless Dreams
Rainbows, Unicorns, and other Fairy Tales in the Land of Serverless DreamsRainbows, Unicorns, and other Fairy Tales in the Land of Serverless Dreams
Rainbows, Unicorns, and other Fairy Tales in the Land of Serverless DreamsJosh Carlisle
 
Frameworks Galore: A Pragmatic Review
Frameworks Galore: A Pragmatic ReviewFrameworks Galore: A Pragmatic Review
Frameworks Galore: A Pragmatic Reviewnetc2012
 
Boost the Performance of SharePoint Today!
Boost the Performance of SharePoint Today!Boost the Performance of SharePoint Today!
Boost the Performance of SharePoint Today!Brian Culver
 
oracle ebs free web service integration tools
oracle ebs free web service integration toolsoracle ebs free web service integration tools
oracle ebs free web service integration toolsSmartDog Services
 
Designing your API Server for mobile apps
Designing your API Server for mobile appsDesigning your API Server for mobile apps
Designing your API Server for mobile appsMugunth Kumar
 
Moving to the Cloud: AWS, Zend, RightScale
Moving to the Cloud: AWS, Zend, RightScaleMoving to the Cloud: AWS, Zend, RightScale
Moving to the Cloud: AWS, Zend, RightScalemmoline
 
NoSQL and CouchDB: the view from MOO
NoSQL and CouchDB: the view from MOONoSQL and CouchDB: the view from MOO
NoSQL and CouchDB: the view from MOOJames Hollingworth
 
Overview of REST - Raihan Ullah
Overview of REST - Raihan UllahOverview of REST - Raihan Ullah
Overview of REST - Raihan UllahCefalo
 
David Max SATURN 2018 - Migrating from Oracle to Espresso
David Max SATURN 2018 - Migrating from Oracle to EspressoDavid Max SATURN 2018 - Migrating from Oracle to Espresso
David Max SATURN 2018 - Migrating from Oracle to EspressoDavid Max
 

Semelhante a Service-Oriented Design and Implement with Rails3 (20)

Service-oriented architecture
Service-oriented architectureService-oriented architecture
Service-oriented architecture
 
Dropping ACID: Wrapping Your Mind Around NoSQL Databases
Dropping ACID: Wrapping Your Mind Around NoSQL DatabasesDropping ACID: Wrapping Your Mind Around NoSQL Databases
Dropping ACID: Wrapping Your Mind Around NoSQL Databases
 
Cross-platform interaction
Cross-platform interactionCross-platform interaction
Cross-platform interaction
 
Restful风格ž„web服务架构
Restful风格ž„web服务架构Restful风格ž„web服务架构
Restful风格ž„web服务架构
 
Alfresco Tech Talk Live (Episode 70): Customizing Alfresco Share 4.2
Alfresco Tech Talk Live (Episode 70): Customizing Alfresco Share 4.2Alfresco Tech Talk Live (Episode 70): Customizing Alfresco Share 4.2
Alfresco Tech Talk Live (Episode 70): Customizing Alfresco Share 4.2
 
Lecture #5 Introduction to rails
Lecture #5 Introduction to railsLecture #5 Introduction to rails
Lecture #5 Introduction to rails
 
Introduction to Rails by Evgeniy Hinyuk
Introduction to Rails by Evgeniy HinyukIntroduction to Rails by Evgeniy Hinyuk
Introduction to Rails by Evgeniy Hinyuk
 
Building Software Backend (Web API)
Building Software Backend (Web API)Building Software Backend (Web API)
Building Software Backend (Web API)
 
Microservices and Best Practices
Microservices and Best Practices Microservices and Best Practices
Microservices and Best Practices
 
Rainbows, Unicorns, and other Fairy Tales in the Land of Serverless Dreams
Rainbows, Unicorns, and other Fairy Tales in the Land of Serverless DreamsRainbows, Unicorns, and other Fairy Tales in the Land of Serverless Dreams
Rainbows, Unicorns, and other Fairy Tales in the Land of Serverless Dreams
 
Frameworks Galore: A Pragmatic Review
Frameworks Galore: A Pragmatic ReviewFrameworks Galore: A Pragmatic Review
Frameworks Galore: A Pragmatic Review
 
Boost the Performance of SharePoint Today!
Boost the Performance of SharePoint Today!Boost the Performance of SharePoint Today!
Boost the Performance of SharePoint Today!
 
oracle ebs free web service integration tools
oracle ebs free web service integration toolsoracle ebs free web service integration tools
oracle ebs free web service integration tools
 
Designing your API Server for mobile apps
Designing your API Server for mobile appsDesigning your API Server for mobile apps
Designing your API Server for mobile apps
 
Moving to the Cloud: AWS, Zend, RightScale
Moving to the Cloud: AWS, Zend, RightScaleMoving to the Cloud: AWS, Zend, RightScale
Moving to the Cloud: AWS, Zend, RightScale
 
Revision
RevisionRevision
Revision
 
NoSQL and CouchDB: the view from MOO
NoSQL and CouchDB: the view from MOONoSQL and CouchDB: the view from MOO
NoSQL and CouchDB: the view from MOO
 
Overview of REST - Raihan Ullah
Overview of REST - Raihan UllahOverview of REST - Raihan Ullah
Overview of REST - Raihan Ullah
 
David Max SATURN 2018 - Migrating from Oracle to Espresso
David Max SATURN 2018 - Migrating from Oracle to EspressoDavid Max SATURN 2018 - Migrating from Oracle to Espresso
David Max SATURN 2018 - Migrating from Oracle to Espresso
 
From 0 to syncing
From 0 to syncingFrom 0 to syncing
From 0 to syncing
 

Mais de Wen-Tien Chang

⼤語⾔模型 LLM 應⽤開發入⾨
⼤語⾔模型 LLM 應⽤開發入⾨⼤語⾔模型 LLM 應⽤開發入⾨
⼤語⾔模型 LLM 應⽤開發入⾨Wen-Tien Chang
 
Ruby Rails 老司機帶飛
Ruby Rails 老司機帶飛Ruby Rails 老司機帶飛
Ruby Rails 老司機帶飛Wen-Tien Chang
 
A brief introduction to Machine Learning
A brief introduction to Machine LearningA brief introduction to Machine Learning
A brief introduction to Machine LearningWen-Tien Chang
 
淺談 Startup 公司的軟體開發流程 v2
淺談 Startup 公司的軟體開發流程 v2淺談 Startup 公司的軟體開發流程 v2
淺談 Startup 公司的軟體開發流程 v2Wen-Tien Chang
 
RSpec on Rails Tutorial
RSpec on Rails TutorialRSpec on Rails Tutorial
RSpec on Rails TutorialWen-Tien Chang
 
ALPHAhackathon: How to collaborate
ALPHAhackathon: How to collaborateALPHAhackathon: How to collaborate
ALPHAhackathon: How to collaborateWen-Tien Chang
 
Git 版本控制系統 -- 從微觀到宏觀
Git 版本控制系統 -- 從微觀到宏觀Git 版本控制系統 -- 從微觀到宏觀
Git 版本控制系統 -- 從微觀到宏觀Wen-Tien Chang
 
Exception Handling: Designing Robust Software in Ruby (with presentation note)
Exception Handling: Designing Robust Software in Ruby (with presentation note)Exception Handling: Designing Robust Software in Ruby (with presentation note)
Exception Handling: Designing Robust Software in Ruby (with presentation note)Wen-Tien Chang
 
Exception Handling: Designing Robust Software in Ruby
Exception Handling: Designing Robust Software in RubyException Handling: Designing Robust Software in Ruby
Exception Handling: Designing Robust Software in RubyWen-Tien Chang
 
從 Classes 到 Objects: 那些 OOP 教我的事
從 Classes 到 Objects: 那些 OOP 教我的事從 Classes 到 Objects: 那些 OOP 教我的事
從 Classes 到 Objects: 那些 OOP 教我的事Wen-Tien Chang
 
Yet another introduction to Git - from the bottom up
Yet another introduction to Git - from the bottom upYet another introduction to Git - from the bottom up
Yet another introduction to Git - from the bottom upWen-Tien Chang
 
A brief introduction to Vagrant – 原來 VirtualBox 可以這樣玩
A brief introduction to Vagrant – 原來 VirtualBox 可以這樣玩A brief introduction to Vagrant – 原來 VirtualBox 可以這樣玩
A brief introduction to Vagrant – 原來 VirtualBox 可以這樣玩Wen-Tien Chang
 
Ruby 程式語言綜覽簡介
Ruby 程式語言綜覽簡介Ruby 程式語言綜覽簡介
Ruby 程式語言綜覽簡介Wen-Tien Chang
 
A brief introduction to SPDY - 邁向 HTTP/2.0
A brief introduction to SPDY - 邁向 HTTP/2.0A brief introduction to SPDY - 邁向 HTTP/2.0
A brief introduction to SPDY - 邁向 HTTP/2.0Wen-Tien Chang
 
RubyConf Taiwan 2012 Opening & Closing
RubyConf Taiwan 2012 Opening & ClosingRubyConf Taiwan 2012 Opening & Closing
RubyConf Taiwan 2012 Opening & ClosingWen-Tien Chang
 
從 Scrum 到 Kanban: 為什麼 Scrum 不適合 Lean Startup
從 Scrum 到 Kanban: 為什麼 Scrum 不適合 Lean Startup從 Scrum 到 Kanban: 為什麼 Scrum 不適合 Lean Startup
從 Scrum 到 Kanban: 為什麼 Scrum 不適合 Lean StartupWen-Tien Chang
 
那些 Functional Programming 教我的事
那些 Functional Programming 教我的事那些 Functional Programming 教我的事
那些 Functional Programming 教我的事Wen-Tien Chang
 
RubyConf Taiwan 2011 Opening & Closing
RubyConf Taiwan 2011 Opening & ClosingRubyConf Taiwan 2011 Opening & Closing
RubyConf Taiwan 2011 Opening & ClosingWen-Tien Chang
 

Mais de Wen-Tien Chang (20)

⼤語⾔模型 LLM 應⽤開發入⾨
⼤語⾔模型 LLM 應⽤開發入⾨⼤語⾔模型 LLM 應⽤開發入⾨
⼤語⾔模型 LLM 應⽤開發入⾨
 
Ruby Rails 老司機帶飛
Ruby Rails 老司機帶飛Ruby Rails 老司機帶飛
Ruby Rails 老司機帶飛
 
A brief introduction to Machine Learning
A brief introduction to Machine LearningA brief introduction to Machine Learning
A brief introduction to Machine Learning
 
淺談 Startup 公司的軟體開發流程 v2
淺談 Startup 公司的軟體開發流程 v2淺談 Startup 公司的軟體開發流程 v2
淺談 Startup 公司的軟體開發流程 v2
 
RSpec on Rails Tutorial
RSpec on Rails TutorialRSpec on Rails Tutorial
RSpec on Rails Tutorial
 
RSpec & TDD Tutorial
RSpec & TDD TutorialRSpec & TDD Tutorial
RSpec & TDD Tutorial
 
ALPHAhackathon: How to collaborate
ALPHAhackathon: How to collaborateALPHAhackathon: How to collaborate
ALPHAhackathon: How to collaborate
 
Git 版本控制系統 -- 從微觀到宏觀
Git 版本控制系統 -- 從微觀到宏觀Git 版本控制系統 -- 從微觀到宏觀
Git 版本控制系統 -- 從微觀到宏觀
 
Exception Handling: Designing Robust Software in Ruby (with presentation note)
Exception Handling: Designing Robust Software in Ruby (with presentation note)Exception Handling: Designing Robust Software in Ruby (with presentation note)
Exception Handling: Designing Robust Software in Ruby (with presentation note)
 
Exception Handling: Designing Robust Software in Ruby
Exception Handling: Designing Robust Software in RubyException Handling: Designing Robust Software in Ruby
Exception Handling: Designing Robust Software in Ruby
 
從 Classes 到 Objects: 那些 OOP 教我的事
從 Classes 到 Objects: 那些 OOP 教我的事從 Classes 到 Objects: 那些 OOP 教我的事
從 Classes 到 Objects: 那些 OOP 教我的事
 
Yet another introduction to Git - from the bottom up
Yet another introduction to Git - from the bottom upYet another introduction to Git - from the bottom up
Yet another introduction to Git - from the bottom up
 
A brief introduction to Vagrant – 原來 VirtualBox 可以這樣玩
A brief introduction to Vagrant – 原來 VirtualBox 可以這樣玩A brief introduction to Vagrant – 原來 VirtualBox 可以這樣玩
A brief introduction to Vagrant – 原來 VirtualBox 可以這樣玩
 
Ruby 程式語言綜覽簡介
Ruby 程式語言綜覽簡介Ruby 程式語言綜覽簡介
Ruby 程式語言綜覽簡介
 
A brief introduction to SPDY - 邁向 HTTP/2.0
A brief introduction to SPDY - 邁向 HTTP/2.0A brief introduction to SPDY - 邁向 HTTP/2.0
A brief introduction to SPDY - 邁向 HTTP/2.0
 
RubyConf Taiwan 2012 Opening & Closing
RubyConf Taiwan 2012 Opening & ClosingRubyConf Taiwan 2012 Opening & Closing
RubyConf Taiwan 2012 Opening & Closing
 
從 Scrum 到 Kanban: 為什麼 Scrum 不適合 Lean Startup
從 Scrum 到 Kanban: 為什麼 Scrum 不適合 Lean Startup從 Scrum 到 Kanban: 為什麼 Scrum 不適合 Lean Startup
從 Scrum 到 Kanban: 為什麼 Scrum 不適合 Lean Startup
 
Git Tutorial 教學
Git Tutorial 教學Git Tutorial 教學
Git Tutorial 教學
 
那些 Functional Programming 教我的事
那些 Functional Programming 教我的事那些 Functional Programming 教我的事
那些 Functional Programming 教我的事
 
RubyConf Taiwan 2011 Opening & Closing
RubyConf Taiwan 2011 Opening & ClosingRubyConf Taiwan 2011 Opening & Closing
RubyConf Taiwan 2011 Opening & Closing
 

Último

Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesThousandEyes
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfpanagenda
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationKnoldus Inc.
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditSkynet Technologies
 

Último (20)

Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog Presentation
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance Audit
 

Service-Oriented Design and Implement with Rails3

  • 1. Service-Oriented Design and Implement with Rails 3 ihower @ Ruby Tuesday 2010/12/15
  • 2. About Me • a.k.a. ihower • http://ihower.tw • http://twitter.com/ihower • Rails Developer since 2006 • The Organizer of Ruby Taiwan Community • http://ruby.tw • http://rubyconf.tw
  • 3. Agenda • What’s SOA • Why SOA • Considerations • The tool set overview • Service side implement • Client side implement • Library packaging • Caching
  • 4. What’s SOA Service oriented architectures • “monolithic” approach is not enough • SOA is a way to design complex applications by splitting out major components into individual services and communicating via APIs. • a service is a vertical slice of functionality: database, application code and caching layer
  • 5. a monolithic web app example request Load Balancer WebApps Database
  • 6. a SOA example request Load request Balancer WebApp WebApps for Administration for User Services A Services B Database Database
  • 7. Why SOA? Isolation • Shared Resources • Encapsulation • Scalability • Interoperability • Reuse • Testability • Reduce Local Complexity
  • 8. Shared Resources • Different front-web website use the same resource. • SOA help you avoiding duplication databases and code. • Why not only shared database? • code is not DRY WebApp for Administration WebApps for User • caching will be problematic Database
  • 9. Encapsulation • you can change underly implementation in services without affect other parts of system • upgrade library • upgrade to Ruby 1.9 • upgrade to Rails 3 • you can provide API versioning
  • 10. Scalability1: Partitioned Data Provides • Database is the first bottleneck, a single DB server can not scale. SOA help you reduce database load • Anti-pattern: only split the database • model relationship is broken WebApps • referential integrity • increase code complexity Database A Database B • Myth: database replication can not help you speed and consistency
  • 11. Scalability 2: Caching • SOA help you design caching system easier • Cache data at the right place and expire at the right times • Cache logical model, not physical • You do not need cache view everywhere
  • 12. Scalability 3: Efficient • Different components have different task loading, SOA can scale by service. WebApps Load Balancer Load Balancer Services A Services A Services B Services B Services B Services B
  • 13. Security • Different services can be inside different firewall • You can only open public web and services, others are inside firewall.
  • 14. Interoperability • HTTP is the most common interface, SOA help you integrate them: • Multiple languages • Internal system e.g. Full-text searching engine • Legacy database, system • External vendors
  • 15. Reuse • Reuse across multiple applications • Reuse for public APIs • Example: Amazon Web Services (AWS)
  • 16. Testability • Isolate problem • Mocking API calls • Reduce the time to run test suite
  • 17. Reduce Local Complexity • Team modularity along the same module splits as your software • Understandability: The amount of code is minimized to a quantity understandable by a small team • Source code control
  • 18. Design considerations • Partition into Separate Services • API Design • Which Protocol
  • 19. How to partition into Separate Services • Partitioning on Logical Function • Partitioning on Read/Write Frequencies • Partitioning on Minimizing Joins • Partitioning on Iteration Speed
  • 20. on Iteration Speed • Which parts of the app have clear defined requirements and design? • Identify the parts of the application which are unlikely to change. • For example: The first version data storage is using MySQL, but may change to NoSQL in the future without affecting front-app.
  • 21. on Logical Function • Higher-level feature services • articles, photos, bookmarks...etc • Low-level infrastructure services • a shared key-value store, queue system
  • 22. On Read/Write Frequencies • Ideally, a service will have to work only with a single data store • High read and low write: the service should optimize a caching strategy. • High write and low read: don’t bother with caching
  • 23. On Join Frequency • Minimize cross-service joins. • But almost all data in an app is joined to something else. • How often particular joins occur? by read/ write frequency and logical separation. • Replicate data across services (For example: a activity stream by using messaging)
  • 24. API Design Guideline • Send Everything you need • Unlike OOP has lots of finely grained method calls • Parallel HTTP requests • for multiple service requests • Send as Little as Possible • Avoid expensive XML
  • 25. Versioning • Be able run multiple versions in parallel: Clients have time to upgrade rather than having to upgrade both client and server in locks step. • Ideally, you won’t have to run multiple versions for very long • Two solutions: • Including a Version in URIs • Using Accept Headers for Versioning (disadvantage: HTTP caching)
  • 26. Physical Models & Logical Models • Physical models are mapped to database tables through ORM. (It’s 3NF) • Logical models are mapped to your business problem. (External API use it) • Logical models are mapped to physical models by you.
  • 27. Logical Models • Not relational or normalized • Maintainability • can change with no change to data store • can stay the same while the data store changes • Better fit for REST interfaces • Better caching
  • 28. Which Protocol? • SOAP • XML-RPC • REST
  • 29. RESTful Web services • Rails way • Easy to use and implement • REST is about resources • URI • HTTP Verbs: GET/PUT/POST/DELETE • Representations: HTML, XML, JSON...etc
  • 30. The tool set • Web framework • XML Parser • JSON Parser • HTTP Client • Model library
  • 31. Web framework • Ruby on Rails, but we don’t need afull features. (Rails3 can be customized because it’s lot more modular. We will discuss it later) • Sinatra: a lightweight framework • Rack: a minimal Ruby webserver interface library
  • 32. ActiveResource • Mapping RESTful resources as models in a Rails application. • Use XML by default • But not useful in practice, why?
  • 33. XML parser • http://nokogiri.org/ • Nokogiri ( ) is an HTML, XML, SAX, and Reader parser. Among Nokogiri’s many features is the ability to search documents via XPath or CSS3 selectors.
  • 34. JSON Parser • http://github.com/brianmario/yajl-ruby/ • An extremely efficient streaming JSON parsing and encoding library. Ruby C bindings to Yajl
  • 35. HTTP Client • How to run requests in parallel? • Asynchronous I/O • Reactor pattern (EventMachine) • Multi-threading • JRuby
  • 36. Typhoeus http://github.com/pauldix/typhoeus/ • A Ruby library with native C extensions to libcurl and libcurl-multi. • Typhoeus runs HTTP requests in parallel while cleanly encapsulating handling logic
  • 37. Typhoeus: Quick example response = Typhoeus::Request.get("http://www.pauldix.net") response = Typhoeus::Request.head("http://www.pauldix.net") response = Typhoeus::Request.put("http://localhost:3000/posts/1", :body => "whoo, a body") response = Typhoeus::Request.post("http://localhost:3000/posts", :params => {:title => "test post", :content => "this is my test"}) response = Typhoeus::Request.delete("http://localhost:3000/posts/1")
  • 38. Hydra handles requests but not guaranteed to run in any particular order HYDRA = Typhoeus::HYDRA.new a = nil request1 = Typhoeus::Request.new("http://example1") request1.on_complete do |response| a = response.body end HYDRA.queue(request1) b = nil request2 = Typhoeus::Request.new("http://example1") request2.on_complete do |response| b = response.body end HYDRA.queue(request2) HYDRA.run # a, b are set from here
  • 39. a asynchronous method def foo_asynchronously request = Typhoeus::Request.new( "http://example" ) request.on_complete do |response| result_value = ActiveSupport::JSON.decode(response.body) # do something yield result_value end self.hydra.queue(request) end
  • 40. Usage result = nil foo_asynchronously do |i| result = i end foo_asynchronously do |i| # Do something for i end HYDRA.run # Now you can use result1 and result2
  • 41. a synchronous method def foo result = nil foo_asynchronously { |i| result = i } self.hydra.run result end
  • 42. Physical Models mapping to database directly • ActiveRecord • DataMapper • MongoMapper, MongoId
  • 43. Logical Models • ActiveModel: an interface and modules can be integrated with ActionPack helpers. • http://ihower.tw/blog/archives/4940
  • 44. integrated with helper? • For example: • link_to post_path(@post) • form_for @post • @post.errors
  • 45. A basic model class YourModel extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations def persisted? false end end
  • 46. without validations class YourModel extend ActiveModel::Naming include ActiveModel::Conversion def persisted? false end def valid?() true end def errors @errors ||= ActiveModel::Errors.new(self) end end
  • 47. Many useful modules • MassAssignmentSecurity • Serialization • Callback • AttributeMethods • Dirty • Observing • Translation
  • 48. Serializers class Person include ActiveModel::Serializers::JSON include ActiveModel::Serializers::Xml attr_accessor :name def attributes @attributes ||= {'name' => 'nil'} end end person = Person.new person.serializable_hash # => {"name"=>nil} person.as_json # => {"name"=>nil} person.to_json # => "{"name":null}" person.to_xml # => "<?xml version="1.0" encoding="UTF-8"?> n<serial-person...
  • 49. Mass Assignment class YourModel # ... def initialize(attributes = {}) if attributes.present? attributes.each { |k, v| send("#{k}=", v) if respond_to?("#{k}=") } end end end YourModel.new( :a => 1, :b => 2, :c => 3 )
  • 50. MassAssignmentSecurity class YourModel # ... include ActiveModel::MassAssignmentSecurity attr_accessible :first_name, :last_name def initialize(attributes = {}) if attributes.present? sanitize_for_mass_assignment(attributes).each { |k, v| send("#{k}=", v) if respond_to?("#{k}=") } end end end
  • 51. Scenario we want to implement • an Users web service, which provide basic CRUD functions. • an web application with the Users client library
  • 53. Customized Rails3 • We don’t need some components. • We can customize ActionController • Building a fast, lightweight REST service with Rails 3 http://pivotallabs.com/users/jdean/blog/articles/1419-building-a-fast- lightweight-rest-service-with-rails-3
  • 54. # config/appliction.rb %w( active_record action_controller action_mailer ).each do |framework| begin require "#{framework}/railtie" rescue LoadError end end
  • 55. # config/application.rb [ Rack::Sendfile, ActionDispatch::Flash, ActionDispatch::Session::CookieStore, ActionDispatch::Cookies, ActionDispatch::BestStandardsSupport, Rack::MethodOverride, ActionDispatch::ShowExceptions, ActionDispatch::Static, ActionDispatch::RemoteIp, ActionDispatch::ParamsParser, Rack::Lock, ActionDispatch::Head ].each do |klass| config.middleware.delete klass end # config/environments/production.rb config.middleware.delete ActiveRecord::ConnectionAdapters::ConnectionManagement
  • 56. # /app/controllers/application_controller.rb class ApplicationController < ActionController::Base class ApplicationController < ActionController::Metal include AbstractController::Logger include Rails.application.routes.url_helpers include ActionController::UrlFor include ActionController::Rendering include ActionController::Renderers::All include ActionController::MimeResponds if Rails.env.test? include ActionController::Testing # Rails 2.x compatibility include ActionController::Compatibility end end http://ihower.tw/blog/archives/4561
  • 57. APIs design best practices (1) • Routing doesn't need Rails resources mechanism , but APIs design should follow RESTful. (This is because we don't have view in service and we don't need URL helpers. So use resources mechanism is too overkill) • RESTful APIs is stateless, each APIs should be independent. So, requests which have dependency relationship should be combined into one API request. (atomic)
  • 58. APIs design best practices (2) • The best format in most case is JSON. ( one disadvantage is we can’t return binary data directly. ) • Use Yajl as parser. # config/application.rb ActiveSupport::JSON.backend = "Yajl" • Don't convert data to JSON in Model, the converting process to JSON should be place in Controller.
  • 59. APIs design best practices (3) • I suggest it shouldn't include_root_in_json # config/application.rb ActiveRecord::Base.include_root_in_json = false • Please notice “the key is JSON must be string”. whether you use symbol or string in Ruby, after JSON encode should all be string. • related key format should be xxx_id or xxx_ids for example: { "user_id" => 4, "product_ids" => [1,2,5] }.to_json • return user_uri field in addition to the user_id field if need
  • 60. a return data example model.to_json and model.to_xml is easy to use, but not useful in practice. # one record { :name => "a" }.to_json # collection { :collection => [ { :name => "a" } , { :name => "b" } ], :total => 123 }.to_json If you want to have pagination, you need total number.
  • 61. APIs design best practices (4) • except return collection, we can also provide Multi-Gets API. through params : ids. ex. /users?ids=2,5,11,23 • client should sort ID first, so we can design cache mechanism much easier. • another topic need to concern is the URL length of GET. So this API can also use POST.
  • 62. an error message return example { :message => "faild", :error_codes => [1,2,3], :errors => ["k1" => "v1", "k2" => "v2" ] }.to_json
  • 63. APIs design best practices (5) • error_codes & errors is optional, you can define it if you need. • errors is used to put model's validation error : model.errors.to_json
  • 64. HTTP status code We should return suitable HTTP status code • 200 OK • 201 Created ( add success) • 202 Accepted ( receive success but not process yet, in queue now ) • 400 Bad Request ( ex. Model Validation Error or wrong parameters ) • 401 Unauthorized
  • 65. class PeopleController < ApplicationController def index @people = Person.paginate(:per_page => params[:per_page] || 20, :page => params[:page]) render :json => { :collection => @people, :total => @people.total_entries }.to_json end def show @person = Person.find( params[:id] ) render :json => @person.to_json end def create @person = Person.new( :name => params[:name], :bio => params[:bio], :user_id => params[:user_id] ) @person.save! render :json => { :id => @person.id }.to_json, :status => 201 end def update @person = user_Person.find( params[:id] ) @person.attributes = { :name => params[:name], :bio => params[:bio], :user_id => params[:user_id] } @person.save! render :status => 200, :text => "OK" end def destroy @person = Person.find( params[:id] ) @person.destroy render :status => 200, :text => "OK" end end
  • 67. Note • No active_record, we get data from service through HTTP client (typhoeus) • Model can include some ActiveModel, modules so we can develop more efficiently. • This model is logical model, mapping to the data from API, not database table. It's different to service's physical model ( ORM- based)
  • 68. # config/appliction.rb %w( action_controller action_mailer ).each do |framework| begin require "#{framework}/railtie" rescue LoadError end end
  • 69. Setup a global Hydry # config/initializers/setup_hydra.rb HYDRA = Typhoeus::Hydra.new
  • 70. An example you can inherited from (1) class LogicalModel extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Serializers::JSON include ActiveModel::Validations include ActiveModel::MassAssignmentSecurity self.include_root_in_json = false # continued... end
  • 71. class LogicalModel An example you can # continued... inherited from (2) def self.attribute_keys=(keys) @attribute_keys = keys attr_accessor *keys end def self.attribute_keys @attribute_keys end class << self attr_accessor :host, :hydra end def persisted? !!self.id end def initialize(attributes={}) self.attributes = attributes end def attributes self.class.attribute_keys.inject(ActiveSupport::HashWithIndifferentAccess.new) do |result, key| result[key] = read_attribute_for_validation(key) result end end def attributes=(attrs) sanitize_for_mass_assignment(attrs).each { |k, v| send("#{k}=", v) if respond_to?("#{k}=") } end
  • 72. Model usage example class Person < LogicalModel self.attribute_keys = [:id, :name, :bio, :user_id, :created_at, :updated_at] self.host = PEOPLE_SERVICE_HOST self.hydra = HYDRA validates_presence_of :title, :url, :user_id # ... end
  • 73. class Person < LogicalModel # ... paginate def self.people_uri "http://#{self.host}/apis/v1/people.json" end def self.async_paginate(options={}) options[:page] ||= 1 options[:per_page] ||= 20 request = Typhoeus::Request.new(people_uri, :params => options) request.on_complete do |response| if response.code >= 200 && response.code < 400 log_ok(response) result_set = self.from_json(response.body) collection = result_set[:collection].paginate( :total_entries => result_set[:total] ) collection.current_page = options[:page] yield collection else log_failed(response) end end self.hydra.queue(request) end def self.paginate(options={}) result = nil async_paginate(options) { |i| result = i } self.hydra.run result end end
  • 74. will_paginate hack! • in order to use will_paginate's helper, we must set current_page manually, so we hack this way: # /config/initializers/hack_will_paginate.rb # This is because our search result via HTTP API is an array and need be paginated. # So we need assign current_page, unless it will be always 1. module WillPaginate class Collection def current_page=(s) @current_page = s.to_i end end end
  • 75. from_json & logging class LogicalModel # ... def self.from_json(json_string) parsed = ActiveSupport::JSON.decode(json_string) collection = parsed["collection"].map { |i| self.new(i) } return { :collection => collection, :total => parsed["total"].to_i } end def self.log_ok(response) Rails.logger.info("#{response.code} #{response.request.url} in #{response.time}s") end def self.log_failed(response) msg = "#{response.code} #{response.request.url} in #{response.time}s FAILED: #{ActiveSupport::JSON.decode (response.body)["message"]}" Rails.logger.warn(msg) end def log_ok(response) self.class.log_ok(response) end def log_failed(response) self.class.log_failed(response) end end
  • 76. class Person < LogicalModel # ... find def self.person_uri(id) "http://#{self.host}/apis/v1/people/#{id}.json" end def self.async_find(id) request = Typhoeus::Request.new( person_uri(id) ) request.on_complete do |response| if response.code >= 200 && response.code < 400 log_ok(response) yield self.new.from_json(response.body) else log_failed(response) end end This from_json is defined by ActiveModel::Serializers::JSON self.hydra.queue(request) end def self.find(id) result = nil async_find(id) { |i| result = i } self.hydra.run result end end
  • 77. class Person < LogicalModel create&update # ... def create return false unless valid? response = Typhoeus::Request.post( self.class.people_uri, :params => self.attributes ) if response.code == 201 log_ok(response) self.id = ActiveSupport::JSON.decode(response.body)["id"] return self else log_failed(response) return nil end end def update(attributes) self.attributes = attributes return false unless valid? response = Typhoeus::Request.put( self.class.person_uri(id), :params => self.attributes ) if response.code == 200 log_ok(response) return self else log_failed(response) Normally data writes do not return nil need to occur in parallel end end end Or write to a messaging system asynchronously
  • 78. delete&destroy class Person < LogicalModel # ... def self.delete(id) response = Typhoeus::Request.delete( self.person_uri(id) ) if response.code == 200 log_ok(response) return self else log_failed(response) return nil end end def destroy self.class.delete(self.id) end end
  • 79. Service client Library packaging • Write users.gemspec file • gem build users.gemspec • distribution • http://rubygems.org • build your local gem server • http://ihower.tw/blog/archives/4496
  • 80. About caching • Internally • Memcached • Externally: HTTP Caching • Rack-Cache,Varnish, Squid
  • 82.
  • 83. References • Books&Articles: • Service-Oriented Design with Ruby and Rails, Paul Dix (Addison Wesley) • Enterprise Rails, Dan Chak (O’Reilly) • RESTful Web Services, Richardson&Ruby (O’Reilly) • RESTful WEb Services Cookbook, Allamaraju&Amundsen (O’Reilly) • Blog: • Rails3: ActiveModel http://ihower.tw/blog/archives/4940 • Rubygems http://ihower.tw/blog/archives/4496 • Rails3: Railtie Plugins http://ihower.tw/blog/archives/4873 • Slides: • Distributed Ruby and Rails http://ihower.tw/blog/archives/3589