Engineering May 24, 2018 4 min read

Interactor Objects

Interactor Objects

As time goes by and your application grows, your code will become increasingly more complex. This means that there will be a lot more logic in places where it shouldn’t be. One example of this are the controllers. Your controller actions will require to do more stuff and be more complex, but your controller shouldn’t have that much business logic. You can move some of it to your Models, since it will probably be related to a lot of them. But that just makes your Models fatter, which you should try to avoid. So what should you do?

One solution is to use Interactor objects. Interactors, also called service objects, use cases, or operations, are a great way to isolate some piece of logic from your application. This allows you to better manage the complexity of your code, also making that logic easier to reuse in other places.

Interactors should follow the Single Responsibility Principle. This means that each one of your Interactor objects should be responsible for doing one thing, and one thing only, in your application. If you are sending emails and showing notifications when something happens, you should have different Interactors for each of these actions. This makes it so that Interactors are great for interacting with external services, since that should be isolated from the rest of your application.

There exist multiple gems that implement Interactors in ruby, but I’m going to focus on Hanami. Hanami allows you to use only what you need in your projects, without needing the whole framework. In this case we want Hanami’s Interactor, found under Utils, which can be used with the hanami-utils gem.

To be able to create an Interactor with Hanami you need to require the file and include it in your class. From there you just need to create a call method, where your logic will be present.

If for example you wanted to log a User’s action in your database, you could create the following Interactor:

[Code snippet — Medium embed: log_action.rb]

And this Interactor could be used in your controller as follows:

[Code snippet — Medium embed: log_controller.rb]

But this can break in many ways. What if the user doesn’t exist? Well then, this just won’t work. Luckily there is something we can do. Hanami allows us to set conditions to run the code. This validation is done with the valid? method that runs before the call method. If we return false on it, the execution will halt. Here is an example:

[Code snippet — Medium embed: log_action_2.rb]

In this method you pass the same arguments as you do for the call method, and you set the conditions necessary for it to be valid and run the Interactor.

Now what happens if these arguments are not valid and you never run the Interactor. Perhaps you want to do something different in the controller. After all, a part of your logic failed, it makes sense to do something different. So how do you do it? How do you know if everything went smoothly or not? Luckily for us, Interactor returns a Result class whenever it runs. As the name implies this class returns if your run was a success or a failure, and allows you to check this, with the success? and fail? methods respectively. So if you want to do something different in case of failure, you just need to do this in the controller:

[Code snippet — Medium embed: log_controller_2.rb]

But the truth is, this alone won’t really tell you much. All the information you have is that something failed, but you don’t know what it was. No need to worry. The Interactor allows you to set errors and to check those errors on the result when something goes wrong. These errors can be anything, from a simple message, to a Class like an Exception. You can set these errors either in the validation or in the call. Picking up on the previous validation error, you could do:

[Code snippet — Medium embed: log_action_3.rb]

The Result of this would be returned as failure, because the Interactor acts as if the presence of an error means that the call has failed. Even so, we still need to make sure we are not returning this method as true so that we don’t run call. In the call method you can just do something like this:

[Code snippet — Medium embed: log_action_4.rb]

Now you can access these errors in the result simply by calling result.errors to get an array containing all errors, or call result.error if you just want to get the first error.

The only thing missing is returning what we need from the Interactor. After all, if we can’t access the changes made then it wouldn’t be all that useful. In this case, we will want to make use of the log we created in the controller. To do so, all you need to do is expose the Log as an instance variable in the Interactor object. To achieve this, you could add the following to the Interactor:

[Code snippet — Medium embed: log_action_5.rb]

Now the log is available in the payload of the result, but you can call it directly from the result. In the controller you just do:

[Code snippet — Medium embed: log_controller_3.rb]

Conclusion

The Interactor object is a great way to remove logic from the controller and better organize your code. It is also useful when you need to reuse code across multiple controllers. Hanami already implements an Interactor object with a lot of options and use cases, so it can be a good choice if you’re thinking about using Interactors.

I hope this helps you manage the complexity of your code the way it helped me. I would love to hear from you if you have used an Interactor before and how you used it.

I work at Runtime Revolution, where we focus on delivering and maintaining the best possible products to our clients, while learning the most we can along the way.

Runtime RevolutionWe are Rails, mobile and product development experts. We can build your product or work with you on your project.www.runtime-revolution.com