aaronmallen/activeinteractor

RFC Interactor#perform Cycle

aaronmallen opened this issue · 5 comments

Elevator pitch, describe and sell us your feature request

As a contributor,
I want to refactor the perform cycle of an interactor
so that debugging issues with interactors become easier to manage, and we can further reduce responsibilities of objects in the perform cycle.

Is your feature request related to a problem

Currently the interactor perform cycle is a bit confusing and hard to follow. We also carry the same context object throughout the entire cycle even though it really has 3 states: input, runtime, and output. The current interactor cycle looks like this:

interactor_perform_cycle_1_1_1

However the code base doesn't really reflect this cycle neatly. In this epic we should revamp the performance cycle to be easier to follow, and reduce the responsibility of the context object even further by instead creating 3 context objects. We will create an context object on input, runtime, and output.

V2 Interactor Perform Cycle

As we can see from the diagram when an interactor calls .perform we will:

  • set the interactor options
  • initialize a new Input object with the arguments passed to .perform
  • we will validate the input object (like we would with the current context object)
  • if the input object is invalid we immediately fail and return the result
  • if the input object is valid we initialize a runtime object
  • we call #perform
  • we initialize an output object
  • if #perform has called !fail we initiate rollback on the runtime object
  • if #perform is successful we validate the output object
  • if the output object is invalid we initiate rollback on the runtime object
  • if the output object is valid we return the result.

States

Based on this diagram we can separate concerns into two types of objects and a total of 3 instances. The first type of objects are simply a collection of attributes. This would accurately describe the purpose of the input and output objects. No operations are ever actually performed on these two objects and they can be considered stable or frozen.

The second type of object is more volatile and is meant to be mutated during the #perform operation. This would accurately describe the runtime object. It is also never validated. This object should never actually be returned to an end consumer.

Object Generation

We should generate the input object with arguments provided to .new or .perform on an instance of ActiveInteractor::Base, after we run validations that do not specify a validation_context and validations that specify the on: :calling validation_context we would then use a valid input object to generate our runtime object. Once the #perform method has completed we would then use the runtime object to generate the output object and run validations on it that specify the on: :called validation_context

Additional Comments

The code should reflect this diagram as closely as possible to ensure subsequent refactoring and debugging is an easy task.

Issues in this epic

Thank you for sketching it. I like the new approach.

It solves the issue of rolling back interactors that were never performed #214. But this got me thinking if we can break the cycle into even more smaller states.. something like

Interactors-Page-2

I have not thought about all the scenarios yet, but that is a quick sketch of the idea.

@mohameddiaa27 could you clarify how it differs from the proposal?

@mohameddiaa27 could you clarify how it differs from the proposal?

I just realized I sketched a wrong flow. I tried to sketch a detailed version of what I had in mind here (Without zooming into the rollback)

Interactors- Page-3

Now I see that the main difference is how we define a perform cycle but it is pretty much similar flow.

@mohameddiaa27 since most of this logic lives in the ActiveInteractor::Interactor::Worker which is a private API I wonder if we can do this in a backwards compatible way. My main concerns will be how we would handle the current call back methods, and how we would handle validation methods on context classes. We'd obviously want to depreciate the current callback methods but want to ensure they work until the release of 2.0...I'm not certain how we would handle the validation methods though.

If you'd like to start writing issues to tackle this epic I'd be more than happy to offer feedback on the stories.