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

    View Slide

  2. ANDY APPLETON
    @appltn

    View Slide

  3. View Slide

  4. BACKGROUND

    View Slide

  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

    View Slide

  6. LOOKS LIKE
    A STATE MACHINE

    View Slide

  7. class Payment < ActiveRecord::Base

    has_many :payment_actions


    def mark_as_submitted

    self.payment_actions.create(..)

    end

    end


    View Slide

  8. DAY 0
    Mandate
    created
    DAY 1
    Payer's bank
    receives mandate
    DAY 2
    Mandate
    approved
    (DAY 3)
    Merchant notified
    of failure

    View Slide

  9. class Mandate < ActiveRecord::Base

    has_many :mandate_histories


    def mark_as_submitted

    self.mandate_histories.create(..)
    end

    end


    View Slide

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

    View Slide


  11. NEXT ON THE
    ROADMAP

    View Slide

  12. EURO PAYMENTS
    FOLLOW A DIFFERENT
    TIMELINE

    View Slide

  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


    View Slide

  14. TIME FOR
    SOME THINKING

    View Slide

  15. TIME FOR
    A STATE MACHINE
    ABSTRACTION

    View Slide

  16. AUDIT TRAIL
    DATA INTEGRITY
    COMPOSABLE
    AUDIT TRAIL
    DATA INTEGRITY

    View Slide

  17. NOTHING REALLY
    HIT THE MARK

    View Slide

  18. View Slide

  19. class PaymentStateMachine

    include Statesman::Machine


    state :pending, initial: true

    state :submitted


    transition from: :pending,

    to: :submitted

    end

    View Slide

  20. class Payment < ActiveRecord::Base

    has_many :payment_actions

    # ...

    def state_machine

    @state_machine ||=
    PaymentStateMachine.new(self,

    transition_class: PaymentAction)

    end

    end

    View Slide

  21. class PaymentStateMachine

    # ...

    guard_transition to: :paid do |pmnt|

    pmnt.has_active_mandate?

    end

    end


    View Slide

  22. class PaymentStateMachine

    # ...

    after_transition to: :paid do |pmnt|

    PaymentNotifier.send_mail(pmnt)

    end

    end


    View Slide

  23. BACKEND AGNOSTIC
    EASY TO ADD AN
    ADAPTER
    BACKEND AGNOSTIC

    View Slide

  24. /gocardless/statesman

    View Slide

  25. ?

    View Slide