Slide 1

Slide 1 text

ActiveJob A Service-Oriented Architecture?

Slide 2

Slide 2 text

@ANDYCROLL

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

ACTIVEWHATNOW?

Slide 5

Slide 5 text

–DHH “The headline feature for Rails 4.2 is the brand new Active Job framework”

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

class MyJob < ActiveJob::Base queue_as :queue_name def perform(active_record_object) # do stuff end end

Slide 8

Slide 8 text

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)

Slide 9

Slide 9 text

EXTRA STUFF for ACTIONMAILER

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

EXTRA STUFF new GLOBAL_ID

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

In SUMMARY? Good Thing

Slide 16

Slide 16 text

SERVICE OBJECTS A SONG OF ice & fire

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

but, Everyone Loves a PORO

Slide 20

Slide 20 text

PLAIN OLD RUBY OBJECTS

Slide 21

Slide 21 text

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/

Slide 22

Slide 22 text

CONVENTIONS • app/services/ • Verbs for naming • Typically one public method per class • Do one ‘business thing’

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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)

Slide 27

Slide 27 text

And ONE MORE…

Slide 28

Slide 28 text

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!

Slide 29

Slide 29 text

ACTIVEJOB
 
 SERVICES

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

SERVICES • app/services/ • Verbs • Typically one public method per class • Do one ‘business thing’

Slide 33

Slide 33 text

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!

Slide 34

Slide 34 text

…caveats

Slide 35

Slide 35 text

SOME Final THOUGHTS

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

“advanced” Architecture can be a MISTAKE

Slide 39

Slide 39 text

use the
 ‘job test’ TO HELP YOUR THINKING

Slide 40

Slide 40 text

YAGNI
 Seriously

Slide 41

Slide 41 text

EMBRACE the Framework

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

Thanks @ANDYCROLL