Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

State machines #1133

Open
peteryates opened this issue Feb 5, 2024 · 0 comments
Open

State machines #1133

peteryates opened this issue Feb 5, 2024 · 0 comments

Comments

@peteryates
Copy link
Member

peteryates commented Feb 5, 2024

We have several places in the app where we'll need to store a record's state:

  • declaration state (eligible, payable, paid, voided, ineligible, awaiting_clawback, clawed_back)
  • outcome state (passed, failed, voided)
  • schedule declaration types (started, retained-1, retained-2, retained-3, retained-4, completed)

Historically in ECF we haven't really dealt with state transitions in a standardised 'controlled' manner, there's a mix of:

These are all ok, but there are some disadvantages:

  • inconsistency means you need to hunt around to see how the statuses work, something made much more difficult by the auto-generated Rails methods (and even more difficult when prefix and suffix are used!)
  • there's no control of which transitions make sense or are valid
  • there's various different sets of tests and validations that all work differently

How do we improve the situation?

Option 1: Use a pre-existing state machine library

Sometimes states shouldn't be able to transition to another state. It might not make sense to transition a cancelled order to despatched, for example. There are several libraries we could use to control this flow in Ruby including:

These all provide some rigidity to the flow of states, integrate nicely with ActiveRecord and offer things like guards and callbacks.

Pros:

  • Well tested, fully fledged state machine library
  • Loads of features

Cons:

  • Complex - we don't need all the features
  • Another dependency

Option 2: Decide on a standard approach and just stick to it

In all likelihood we won't need all of the functionality provided by any of the gems above. We could just implement a basic service object that controls the state and uses regular Rails code to transition.

class Declarations::ChangeState
  attr_reader :declaration

  def initialize(declaration)
    @declaration = declaration
  end

  def pay!
    fail unless declaration.state == "payable"

    # TODO write an event or log something useful, and wrap in a transaction.
    declaration.update!(state: "paid")
  end

  def claw_back!; end

  def mark_ineligible!; end
end

Pros:

  • Simple and bespoke

Cons:

  • We might end up just adding more and more features until we have our own state machines library
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

No branches or pull requests

1 participant