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

RFC Interactor#perform Cycle #291

Closed
aaronmallen opened this issue Oct 28, 2020 · 5 comments
Closed

RFC Interactor#perform Cycle #291

aaronmallen opened this issue Oct 28, 2020 · 5 comments
Labels
epic An epic feature New feature or request rfc Request for comment wontfix This will not be worked on

Comments

@aaronmallen
Copy link
Owner

aaronmallen commented Oct 28, 2020

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

@aaronmallen aaronmallen added feature New feature or request epic An epic labels Oct 28, 2020
@aaronmallen aaronmallen added this to To Do in ActiveInteractor 2.0 (rejected) via automation Oct 28, 2020
@aaronmallen aaronmallen added this to the v1.2.0 milestone Oct 28, 2020
@aka-momo
Copy link
Contributor

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.

@aaronmallen
Copy link
Owner Author

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

@aaronmallen aaronmallen pinned this issue Oct 30, 2020
@aaronmallen aaronmallen changed the title Interactor#perform Cycle RFC Interactor#perform Cycle Oct 30, 2020
@aaronmallen aaronmallen added the rfc Request for comment label Oct 30, 2020
@aaronmallen aaronmallen removed this from the v1.2.0 milestone Oct 30, 2020
@aka-momo
Copy link
Contributor

@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.

@aaronmallen
Copy link
Owner Author

@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.

@aaronmallen
Copy link
Owner Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
epic An epic feature New feature or request rfc Request for comment wontfix This will not be worked on
Projects
No open projects
Development

No branches or pull requests

2 participants