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

Service Oriented Design in Practice

Service Oriented Design in Practice

Implementing a Service Oriented Design with Ruby can lower maintenance costs by increasing isolation, scalability and code reuse. That's the theory, anyway. It's true, but in practice there are lots of nitty gritty details of rolling out a service oriented design that can reduce the expected benefits -- or even leave you worse off than you started.

This talk will shed light on what it takes to successfully leverage a service oriented architecture. Emphasis will be on less discussed considerations like testing strategies, dependency and release management, operations and developer tools rather than the happy path of how to implement and consume services. Examples will be provided from our experience building tool to analyze home energy use in order to help people save on their power bills.

In the end, you'll walk away with a deeper understanding of how to weigh the pros and cons of introducing services into your architecture, and you'll have learned some techniques to make the transition smoother if you decide it's right for you.

Bryan Helmkamp

March 19, 2011
Tweet

More Decks by Bryan Helmkamp

Other Decks in Programming

Transcript

  1. “SOA has become a loaded term. To [many], it implies

    the use of tools such as SOAP, WSDL, WS-*, or XML-RPC. This [is why we use] the word design as opposed to architecture.”
  2. OOP and SRP for systems! Designer can’t boot app What’s

    causing this bug?!? Copy/pasted all deploy scripts We need some rules (and tools) Broke production, tests are green The tests are now a pain to run! Enlightenment?
  3. Five Problems • Designer can’t boot app • Copy/pasted all

    deploy scripts • What’s causing this bug?!? • Broke production, tests are green • The tests are now a pain to run!
  4. e20  tail e20  clone e20  pull e20  status e20  disable

    e20  enable e20  inventory e20  bundle e20  configure e20  migrate e20  bootstrap e20  start e20  stop e20  reset
  5. e20_cap.gemspec lib lib/e20 lib/e20/recipes lib/e20/recipes/bundler.rb lib/e20/recipes/database.rb lib/e20/recipes/defaults.rb lib/e20/recipes/dj.rb lib/e20/recipes/gem.rb lib/e20/recipes/notify.rb

    lib/e20/recipes/resque.rb lib/e20/recipes/symlinks.rb lib/e20/recipes/unicorn.rb lib/e20/recipes/whenever.rb lib/e20/recipes.rb Rakefile README.rdoc
  6. load  'deploy'  #  Capistrano's  default  tasks gem  "e20_cap",  "0.10.0" require

     "e20/recipes" require  "e20/recipes/whenever" set  :application,  "weather_service" set  :auto_tagger_stages,  [:staging,  :production] set  :hoptoad_api_key,  "[key  here]" task  :production  do    set  :rails_env,  'production'    set  :auto_tagger_stage,  :production    role  :app,  "app01-­‐weather.prod"    role  :unicorn,  "app01-­‐weather.prod"    role  :cron,  "app01-­‐weather.prod" end
  7. $  curl  -­‐I  http://calc.stage/v0/users/foo HTTP/1.1  404  Not  Found Server:  nginx

    Date:  Tue,  15  Mar  2011  04:01:11  GMT Content-­‐Type:  application/json;  charset=utf-­‐8 Transfer-­‐Encoding:  chunked Connection:  close Status:  404  Not  Found X-­‐Transaction:  74b471a589084c4a9020a8eaddbfac X-­‐Served-­‐By:  app02-­‐calc.stage X-­‐Revision:  f46e3f13668f320bd3bd8e1e7e9377b9c Cache-­‐Control:  no-­‐cache
  8. $  curl  -­‐I  http://calc.stage/v0/users/foo HTTP/1.1  404  Not  Found Server:  nginx

    Date:  Tue,  15  Mar  2011  04:01:11  GMT Content-­‐Type:  application/json;  charset=utf-­‐8 Transfer-­‐Encoding:  chunked Connection:  close Status:  404  Not  Found X-­‐Transaction:  74b471a589084c4a9020a8eaddbfac X-­‐Served-­‐By:  app02-­‐calc.stage X-­‐Revision:  f46e3f13668f320bd3bd8e1e7e9377b9c Cache-­‐Control:  no-­‐cache
  9. $  curl  -­‐I  http://calc.stage/v0/users/foo HTTP/1.1  404  Not  Found Server:  nginx

    Date:  Tue,  15  Mar  2011  04:01:11  GMT Content-­‐Type:  application/json;  charset=utf-­‐8 Transfer-­‐Encoding:  chunked Connection:  close Status:  404  Not  Found X-­‐Transaction:  74b471a589084c4a9020a8eaddbfac X-­‐Served-­‐By:  app02-­‐calc.stage X-­‐Revision:  f46e3f13668f320bd3bd8e1e7e9377b9c Cache-­‐Control:  no-­‐cache
  10. $  curl  -­‐I  http://calc.stage/v0/users/foo HTTP/1.1  404  Not  Found Server:  nginx

    Date:  Tue,  15  Mar  2011  04:01:11  GMT Content-­‐Type:  application/json;  charset=utf-­‐8 Transfer-­‐Encoding:  chunked Connection:  close Status:  404  Not  Found X-­‐Transaction:  74b471a589084c4a9020a8eaddbfac X-­‐Served-­‐By:  app02-­‐calc.stage X-­‐Revision:  f46e3f13668f320bd3bd8e1e7e9377b9c Cache-­‐Control:  no-­‐cache
  11. Started  GET  "/rewards"  at  Mon  Mar  14  21:06:41   -­‐0700

     2011 [E20::Ops::Middleware]  Transaction  ID:   5c47dc12-­‐0cb1-­‐4aae-­‐82e8-­‐4c9a58a33ca4    Processing  by  RewardsController#show  as  HTML [Calc]  GET  http://calc.prod/users/... [Calc]  200  from  app02-­‐calc.prod  (b010ea1b)  :   5199d9b3-­‐47bf-­‐491c-­‐bef0-­‐d591390a07a8 Rendered  rewards/show.html.haml  (39.4ms) Completed  200  OK  in  58ms
  12. Started  GET  "/rewards"  at  Mon  Mar  14  21:06:41   -­‐0700

     2011 [E20::Ops::Middleware]  Transaction  ID:   5c47dc12-­‐0cb1-­‐4aae-­‐82e8-­‐4c9a58a33ca4    Processing  by  RewardsController#show  as  HTML [Calc]  GET  http://calc.prod/users/... [Calc]  200  from  app02-­‐calc.prod  (b010ea1b)  :   5199d9b3-­‐47bf-­‐491c-­‐bef0-­‐d591390a07a8 Rendered  rewards/show.html.haml  (39.4ms) Completed  200  OK  in  58ms
  13. Started  GET  "/rewards"  at  Mon  Mar  14  21:06:41   -­‐0700

     2011 [E20::Ops::Middleware]  Transaction  ID:   5c47dc12-­‐0cb1-­‐4aae-­‐82e8-­‐4c9a58a33ca4    Processing  by  RewardsController#show  as  HTML [Calc]  GET  http://calc.prod/users/... [Calc]  200  from  app02-­‐calc.prod  (b010ea1b)  :   5199d9b3-­‐47bf-­‐491c-­‐bef0-­‐d591390a07a8 Rendered  rewards/show.html.haml  (39.4ms) Completed  200  OK  in  58ms
  14. Started  GET  "/rewards"  at  Mon  Mar  14  21:06:41   -­‐0700

     2011 [E20::Ops::Middleware]  Transaction  ID:   5c47dc12-­‐0cb1-­‐4aae-­‐82e8-­‐4c9a58a33ca4    Processing  by  RewardsController#show  as  HTML [Calc]  GET  http://calc.prod/users/... [Calc]  200  from  app02-­‐calc.prod  (b010ea1b)  :   5199d9b3-­‐47bf-­‐491c-­‐bef0-­‐d591390a07a8 Rendered  rewards/show.html.haml  (39.4ms) Completed  200  OK  in  58ms
  15. Started  GET  "/rewards"  at  Mon  Mar  14  21:06:41   -­‐0700

     2011 [E20::Ops::Middleware]  Transaction  ID:   5c47dc12-­‐0cb1-­‐4aae-­‐82e8-­‐4c9a58a33ca4    Processing  by  RewardsController#show  as  HTML [Calc]  GET  http://calc.prod/users/... [Calc]  200  from  app02-­‐calc.prod  (b010ea1b)  :   5199d9b3-­‐47bf-­‐491c-­‐bef0-­‐d591390a07a8 Rendered  rewards/show.html.haml  (39.4ms) Completed  200  OK  in  58ms
  16. Started  GET  "/rewards"  at  Mon  Mar  14  21:06:41   -­‐0700

     2011 [E20::Ops::Middleware]  Transaction  ID:   5c47dc12-­‐0cb1-­‐4aae-­‐82e8-­‐4c9a58a33ca4    Processing  by  RewardsController#show  as  HTML [Calc]  GET  http://calc.prod/users/... [Calc]  200  from  app02-­‐calc.prod  (b010ea1b)  :   5199d9b3-­‐47bf-­‐491c-­‐bef0-­‐d591390a07a8 Rendered  rewards/show.html.haml  (39.4ms) Completed  200  OK  in  58ms
  17. Started  GET  "/rewards"  at  Mon  Mar  14  21:06:41   -­‐0700

     2011 [E20::Ops::Middleware]  Transaction  ID:   5c47dc12-­‐0cb1-­‐4aae-­‐82e8-­‐4c9a58a33ca4    Processing  by  RewardsController#show  as  HTML [Calc]  GET  http://calc.prod/users/... [Calc]  200  from  app02-­‐calc.prod  (b010ea1b)  :   5199d9b3-­‐47bf-­‐491c-­‐bef0-­‐d591390a07a8 Rendered  rewards/show.html.haml  (39.4ms) Completed  200  OK  in  58ms
  18. Started  GET  "/rewards"  at  Mon  Mar  14  21:06:41   -­‐0700

     2011 [E20::Ops::Middleware]  Transaction  ID:   5c47dc12-­‐0cb1-­‐4aae-­‐82e8-­‐4c9a58a33ca4    Processing  by  RewardsController#show  as  HTML [Calc]  GET  http://calc.prod/users/... [Calc]  200  from  app02-­‐calc.prod  (b010ea1b)  :   5199d9b3-­‐47bf-­‐491c-­‐bef0-­‐d591390a07a8 Rendered  rewards/show.html.haml  (39.4ms) Completed  200  OK  in  58ms
  19. describe  Calculator::User,  "#bills"  do    before  {  Artifice.activate_with(Rails.application)  }  

     after    {  Artifice.deactivate  }    it  "returns  all  bills  for  the  user"  do        user  =  User.make        1.upto(3)  do  |n|            @user.bills.make(:date  =>  n.months.ago)        end        Calculator::User.new(user.id).should  have(3).bills    end end
  20. describe  Notifications,  ".send_monthly_status"  do    it  "includes  the  kWh  in

     the  email"  do        user  =  User.new        points  =  MonthlyUsagePointsAward.new(            :user  =>  user,            :bill  =>  mock(:electricity  =>  100)        )        Notifications.send_monthly_status(points)        email  =  ActionMailer::Base.deliveries.last        email.subject.should  ==  "You  used  100  kWh."    end end
  21. describe  Notifications,  ".send_monthly_status"  do    it  "includes  the  kWh  in

     the  email"  do        user  =  User.new        points  =  MonthlyUsagePointsAward.new(            :user  =>  user,            :bill  =>  mock(:electricity  =>  100)        )        Notifications.send_monthly_status(points)        email  =  ActionMailer::Base.deliveries.last        email.subject.should  ==  "You  used  100  kWh."    end end
  22.    Scenario:  Sort  actions  by  recommendation        Given

     I  live  in  zip  code  "60608"        When  I  view  my  recommended  actions        Then  the  first  three  actions  should  be:          |  Install  a  tankless  water  heater        |          |  Reduce  water  heater  temperature        |          |  Replace  your  home  lights  with  CFLs  |