[dry-operation] Passing data between steps

Hi folks,
I am working on a proof-of-concept, exploring how we would use dry-operation to refactor some complex business logic.
In our application, operations are complex and operate on many record types, many of which are relations to record references passed in with the parameters.
Some of the steps will derive additional data that is required by later steps.
Our initial implementation has a lot of repetitive boilerplate in the steps that extracts some pieces of relevant data from the input, such as

  • coerced/validated data from the operation’s input parameters
  • record instances derived from the operation’s input parameters
  • data derived from intervening steps that have added it to the operation_result

Are there any recommended patterns for handling moving the data through the steps and accessing it within the steps ?
Any suggestions would be greatly appreciated!

class DoThings < Dry::Operation
  def call(params)
    validated_contract = step validate_contract(params)

    operation_result = step derive_A_from_contract(validated_contract)
    operation_result = step derive_B_from_contract(operation_result)
    operation_result = step do_thing_with_A(operation_result)
    operation_result = step do_other_thing_with_A(operation_result)
    step do_thing_with_B(operation_result)
  end
end

Generally speaking, it’s most desirable to write Operations to be as close to pure functions as you can make them. This makes testability much simpler.

But I hear you about having to build up state over multiple steps. I ran into this situation creating a billing system, and there were a large number of intermediate steps that needed to be coordinated.

Here’s how I’ve approached the problem: define an object for the unit of work, a kind of garbage can of mutable state that gets passed around, and then transform this object to your final representation at the end.

This approach is a synthesis of FP and OOP, using each design philosophy for its relative strengths. Building up state gradually is annoying in pure FP, but OOP is very good for this.