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

Rage against the state machine

Rage against the state machine

From LRUG March 2014 - A story about the problems we faced modelling state and recording state changes at GoCardless and how we generalised our solution to those problems into a new gem, Statesman.

Andy Appleton

March 10, 2014
Tweet

More Decks by Andy Appleton

Other Decks in Programming

Transcript

  1. RAGE AGAINST THE STATE MACHINE

  2. ANDY APPLETON @appltn

  3. None
  4. BACKGROUND

  5. Payment failure submitted DAY 5 DAY 4 Merchant receives failure

    notification DAY 3 Payment to merchant reversed DAY 0 DAY 1 Payer's bank receives request DAY 2 Merchant credited with payment Payment request submitted
  6. LOOKS LIKE A STATE MACHINE

  7. class Payment < ActiveRecord::Base
 has_many :payment_actions
 
 def mark_as_submitted
 self.payment_actions.create(..)


    end
 end

  8. DAY 0 Mandate created DAY 1 Payer's bank receives mandate

    DAY 2 Mandate approved (DAY 3) Merchant notified of failure
  9. class Mandate < ActiveRecord::Base
 has_many :mandate_histories
 
 def mark_as_submitted
 self.mandate_histories.create(..)

    end
 end

  10. DUPLICATION INCONSISTENCY BUT IT WORKS (AND WE HAVE A PRODUCT

    TO BUILD) DUPLICATION INCONSISTENCY
  11. € NEXT ON THE ROADMAP

  12. EURO PAYMENTS FOLLOW A DIFFERENT TIMELINE

  13. class Payment < ActiveRecord::Base
 has_many :payment_actions
 
 def gbp_mark_as_submitted
 self.payment_actions.create(..)


    end
 
 def eur_mark_as_submitted
 self.payment_actions.create(..)
 end
 end

  14. TIME FOR SOME THINKING

  15. TIME FOR A STATE MACHINE ABSTRACTION

  16. AUDIT TRAIL DATA INTEGRITY COMPOSABLE AUDIT TRAIL DATA INTEGRITY

  17. NOTHING REALLY HIT THE MARK

  18. None
  19. class PaymentStateMachine
 include Statesman::Machine
 
 state :pending, initial: true
 state

    :submitted
 
 transition from: :pending,
 to: :submitted
 end
  20. class Payment < ActiveRecord::Base
 has_many :payment_actions
 # ...
 def state_machine


    @state_machine ||= PaymentStateMachine.new(self,
 transition_class: PaymentAction)
 end
 end
  21. class PaymentStateMachine
 # ...
 guard_transition to: :paid do |pmnt|
 pmnt.has_active_mandate?


    end
 end

  22. class PaymentStateMachine
 # ...
 after_transition to: :paid do |pmnt|
 PaymentNotifier.send_mail(pmnt)


    end
 end

  23. BACKEND AGNOSTIC EASY TO ADD AN ADAPTER BACKEND AGNOSTIC

  24. /gocardless/statesman

  25. ?