ActiveJob: A Service Oriented Architecture?

ActiveJob: A Service Oriented Architecture?

RailsConf 2015

Ec9306df417149a7e1e23806a3f7ec60?s=128

Andy Croll

April 23, 2015
Tweet

Transcript

  1. ActiveJob A Service-Oriented Architecture?

  2. @ANDYCROLL

  3. None
  4. ACTIVEWHATNOW?

  5. –DHH “The headline feature for Rails 4.2 is the brand

    new Active Job framework”
  6. None
  7. class MyJob < ActiveJob::Base queue_as :queue_name def perform(active_record_object) # do

    stuff end end
  8. MyJob.perform_now(object) MyJob.perform_later(object) MyJob.set(wait_until: Date.tomorrow) .perform_later(object) MyJob.set(wait: 1.week) .perform_later(object)

  9. EXTRA STUFF for ACTIONMAILER

  10. UserMailer.hi(@user).deliver_now UserMailer.hi(@user).deliver_later

  11. EXTRA STUFF new GLOBAL_ID

  12. class MyJob < ActiveJob::Base def perform(thing_id) thing = Thing.find(thing_id) thing.do_slow_stuff

    end end
  13. class MyJob < ActiveJob::Base def perform(thing) thing.do_slow_stuff end end

  14. None
  15. In SUMMARY? Good Thing

  16. SERVICE OBJECTS A SONG OF ice & fire

  17. There’s A LOT OF CHAT ABOUT ARCHITECTURE inside RAILS APPS

  18. –ME “I’m in DHH’s posse”

  19. but, Everyone Loves a PORO

  20. PLAIN OLD RUBY OBJECTS

  21. WHEN TO USE? • The action is complex • The

    action reaches across multiple models • The action interacts with an external service • The action is not a core concern of the underlying model blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
  22. CONVENTIONS • app/services/ • Verbs for naming • Typically one

    public method per class • Do one ‘business thing’
  23. class AcceptInvite def initialize(invite, user) @invite = invite @user =

    user end def accept invite.accept(@user) log_acceptance(@invite, @user) UserMailer.welcome(@user).deliver AdminMailer.invited(@user).deliver end end
  24. Invite::Accept = Struct.new(:invite, :user) do def run invite.accept(user) log_acceptance(invite, user)

    UserMailer.welcome(user).deliver AdminMailer.invited(user).deliver end end
  25. class InviteAccepterService def initialize(invite, user, logger = MyLogger.new) @invite, @user,

    @logger = invite, user, logger end def process invite.accept(@user) log_acceptance(@invite, @user) UserMailer.welcome(@user).deliver AdminMailer.invited(@user).deliver end def log_acceptance @logger.acceptance(@invite, @user) end end
  26. class AcceptInvite def self.call(invite, user) invite.accept(user) log_acceptance(invite, user) UserMailer.welcome(user).deliver AdminMailer.invited(user).deliver

    end end #=> AcceptInvite.(invite, user)
  27. And ONE MORE…

  28. class AcceptInvite < ActiveJob::Base def perform(invite, user) invite.accept(user) log_acceptance(invite, user)

    UserMailer.welcome(user).deliver_now AdminMailer.invited(user).deliver_now end end LOOK!
  29. ACTIVEJOB
 
 SERVICES

  30. SERVICES • The action is complex • The action reaches

    across multiple models • The action interacts with an external service • The action is not a core concern of the underlying model
  31. JOBS • The action is complex • The action reaches

    across multiple models • The action interacts with an external service • The action is not a core concern of the underlying model
  32. SERVICES • app/services/ • Verbs • Typically one public method

    per class • Do one ‘business thing’
  33. SERVICES & JOBS • app/services/ • Verbs • Typically one

    public method per class • Do one ‘business thing’ • app/jobs/ • Verbs • One ‘perform’ method defined per class • Do one ‘business thing’ • Async or sync, bonus!
  34. …caveats

  35. SOME Final THOUGHTS

  36. class InviteController < ApplicationController def accept AcceptInvite.perform_now(Invite.find(params[:id]), current_user) redirect_to somewhere_else_path

    end end class AcceptInvite < ActiveJob::Base def perform(invite, user) invite.accept(user) log_acceptance(invite, user) UserMailer.welcome(user).deliver_now AdminMailer.invited(user).deliver_now end end
  37. class InviteController < ApplicationController def accept invite = Invite.find(params[:id] invite.accept(current_user)

    log_acceptance(invite, current_user) UserMailer.welcome(current_user).deliver_later AdminMailer.invited(current_user).deliver_later redirect_to somewhere_else_path end end
  38. “advanced” Architecture can be a MISTAKE

  39. use the
 ‘job test’ TO HELP YOUR THINKING

  40. YAGNI
 Seriously

  41. EMBRACE the Framework

  42. –ME, AGAIN “Don’t write code future you will hate you

    for.”
  43. Thanks @ANDYCROLL