Interactors in Ruby — easy as cake, simple as pie

Tiago Farias
Goiabada
Published in
11 min readOct 31, 2017

--

Ugh, I hate to start an article straight with a piece of code. But, really, this is the best way to show you the problem and then a clear solution for it. So, please take a look at this controller action and see if you can spot the problem with it:

A bloated rails controller

Yikes! Classic Fat Controller, right? It is initializing and saving objects, sending emails and creating async jobs. All in one method. Well, we’ve been hearing for a long time now that we shouldn’t be imposing our little methods/classes too many responsibilities, otherwise they could grow up and become sociopaths. Imagine if later on we found out that this controller action needs to validate a couple parameters to decide if the object will be persisted on the database. Well, obviously more code is going to be needed no matter what you do. But does it need to be right there in our already bloated method? “Oh yeah, I’m much better off handling business logic inside my Models, since it’s all there anyway, the queries, the scopes, callbacks, etc.” No, wait, what? Not 2010 anymore?

Interactor to the rescue

Here’s where the Interactor Design Pattern shines with flying colors. Basically, an Interactor is an object that encapsulates business logic (read domain models) for one (and one only) use case. That’s it. It solves one problem for a small part of the domain of the application. It is based on the Command pattern, where each Command class/object represents a task and has one public method. Basically, something like:

Examples of calls for the Command Pattern

I know, it was a very simplistic definition of the Command pattern. Well, here you can find a more formal definition with more examples in case you got curious. But you got the main idea: Interactor is basically a Command object that we can use to reduce bloat in our controllers and models. Service Object is yet another name for it, even though it’s quite general. I mean, there is no clear pattern for it. There are some guidelines, like: “it should be a PORO (Plain Old Ruby Object)” and “try to create it with only one responsibility, if possible” or “minimal public API”, but nothing really enforces it. Having said that, I’ve seen Service Objects with more than one method in their public APIs and, you know what? They were pretty good looking.

By the way, the TrailBlazer framework (which sits on top of Rails) calls these objects Operations. And the dry-rb team created a DSL based on the same idea with a few more features in dry-transactions. But I’d like to use the example of a more popular gem for handling such objects, the gem interactor. And since I’m talking about it, I’ll take the time to highlight some of its strengths and some possible improvements that I think would be feasible. Of course, it’s just my opinion, it’s not written in blood, so hold your horses =).

Simple as pie

The Ruby gem Interactor solves the problem above mentioned in a very very simple way. For each use-case, action or task, whatever you may call it, you create a class named as the purpose of the task (like ReadFile, SaveUserProfile or DeleteAccount) and invoke the method call passing the data needed (or context) for that task, like:

A basic setup example of an interactor class

Two things to setup: include the module Interactor and define a public instance method call. That’s it. This simplicity is one of the reasons I like this gem so much and we use it in many of our projects here at Guava. Besides, if we need to pre-process something before the call method, like setup or validate the arguments, we can do that with the before hook, which accepts a block as a parameter. There are also the after and around hooks.

Now let’s go back to the tasks in the first controller (the bloated one) mentioned in the beginning. Notice the create action is trying to do a list of things in order:

1. create the sale record

2. find and process the stock for the products involved

3. process and send to shipping department

4. send confirmation email

Well, to start off removing business code from our controller, we create 4 interactors: CreateSale, ProcessStock, DispatchShipping and SendConfirmationEmail. If we call each one of these interactors, it’s gonna work just fine. But there’s a caveat: if one of these operations fail, we need to rollback all the other ones already finished. Therefore, if we used this approach, we would end up with a lot of conditional code in our controller, like:

“If ifs and buts were candies and nuts we’d all have a nice code” or whatever.

Not nice. In fact, it’s called conditional hell. Enter Organizers. An Organizer is a special kind of Interactor that has one responsibility: to call other interactors in a given sequence. For the situation above, we would have:

Expressive, ain’t it? It’s like, you open the PlaceOrder class and it’s right there, in simple, defined steps, basically telling you what it does. Each interactor executed passes the context (or arguments) through to the next interactor in line. So, for instance, the organizer in the example above passes only the sale (which we suppose has a lot of data about the products and stuff) as its context to the first interactor, CreateSale. But all these interactors play like a family, they share context. Meaning that if we set a variable productsin our shared context inside CreateSale, ProcessStock and the next interactors in line will all have access to products through the context object. Except if one of the interactors fail, in which case it avoids the next interactor in line of being invoked. There’s more to know in the case of a failure like the above, but I recommend reading the gem’s documentation for that.

Oh, and I did not forget about the transaction code in the bloated controller code example. I’ll talk briefly about how it can be handled in an organizer later.

In the future (probably in version 4) we may be able to pass not only interactors to the organizer to execute, but also procs and method objects! Something like:

Yeah, I think it’s cool to see organizers just receiving a Proc or Method Object and execute it while being impartial about the argument’s race, gender and nationality. We need that in the world. Also, I believe there is a use for that, for instance, if you need to create an organizer dynamically or if the task being passed is so trivial that would justify the use of a Proc (you know, because it’s a one-liner). On the other hand, I think it kinda hurts the purpose a little bit. Naming is important. Naming interactors very much so. I mean, the whole description of the task goes in the name of the class! You can read straightforward what a given interactor is all about just by reading its class name. With this idea in mind, when we read an organizer’s organize call, it becomes very clear where it’s going: CreateSale, ProcessStock, DispatchShipping, SendConfirmationEmail. Now, what if a Proc, even if it’s a one-liner, stands in the middle of the “sentence”? It’s definitely not the same as before. It’s just not as fluent, even though in more appropriate cases I guess it would still be readable. All in all, I think both arguments balance each other out, which makes me vote in favor of the change, leaving the developer responsible for the legibility of their own code.

Sharp as a tack

There are many pearls throughout its features, but one that makes a lot of sense are its hooks: before, around and after. Think about it: this allows you to do whatever you want inside an interactor without much complexity. If you need to setup any object or validate parameters previously to your main method, you can use the before hook. To set warnings, close files or connections after the interactor successfully ran, you can use the afterhook. While around hooks allow you to weave your interactor callin between a block of code, so you can decide exactly when to invoke it. This is useful for so many times when we need to wrap our interactors with extra code. Take Rails’ ActiveRecord transactions for instance. Imagine we have a Rails app where, at some point, we need to save 3 records to a database, say, a Sale, a Stock and a Payment. The thing is: if one of them fails, we need to rollback the transaction in order to avoid inconsistent state in our database. Now, one way to do that would be to use an organizer and call an interactor for each of these tasks. But what if the third interactor, SavePayment, does not go through? We would end up with a sale that was made with no payment! Prison! Death! Or maybe just a slap on the wrist depending on the country where you’re reading this from… But, in other words, if we don’t process the 3 actions inside a single ActiveRecord transaction, we will be in trouble. That’s where the interactor’s aroundhook comes in handy, allowing us to look smarter than we really are. We can simply go with:

Define here what to do before and after the interactor (in this case an organizer)

Light as a feather

The source code of this gem is very small. Just to make a point about this, the lib folder has 5 ruby files. The longest method is 8 lines long. There are only a few conditionals and no ”elses”. Also no metaprogramming, making it very easy to understand the whole code base just by reading it, even if you don’t master ruby. And that simplicity is no coincidence, it’s by design.

For instance, there have been suggestions to implement validations inside interactors in the past. The idea is simple: the interactor needs to validate its context before fulfilling the main task. And some spinoff gems have resulted from deep discussions about this subject, like troupe for example. While I enjoy the solution and have suffered from the same kind of itch that moved this idea forward, I don’t think it goes with interactor’s philosophy. The reason is that interactor is very light, thin, with almost no magic at all, while a DSL implementation to promote context validation… y’know… not so much.

Since interactor has always been striving for simplicity and elegance from the beginning, there must be a more elegant way to provide the context validation ability without inserting too much magic in the code base. Well, I’ve been thinking that, since the context object must be self-contained, and interactor already provides beforehooks, why not just pass a validator via context and evaluate it at the beforehook of the main interactor? For example:

How to validate an interactor’s context? Injecting a “validation interactor”!

Notice that this idea brings up the concept of a Validation Interactor, which unsurprisingly is injected via params and validates an interactor context. It has no magic, really. In the snippet above, I present 2 ways to pass a validator to the call method. The first one would cause a change in the API, which is always harder to deal with. But it keeps context and validator as 2 separated components for the interactor to run (you gotta keep’em separated). The fact that there is a separated parameter for validator just makes more explicit that the validator is NOT part of the context, but another object that was injected and simply knows how to validate his brother, the context object. The second option is way simpler. The validator is part of the context and the gem needs to work a bit to pull the validator out of the context object. This also requires a better documentation, once the validator is implicit.

The cons

Context is global

One thing that I’ve always found kind of clumsy was the Context object passed as parameter in the callmethod. It’s basically a very promiscuous struct object, that will accept anything anytime. It is a global variable. And you know what happened to the little boy that played with global variables, right? Yeah, he died. So, stay away, please. Of course I’m exaggerating, usually there is no big problem with interactors carrying global variables around (unless you’re a purist). Often they’re short lived and the fact that they only have one single responsibility helps a lot. But I can see it getting messy. Imagine that we have an organizer of 5 interactors, each one with its own rollback hook set up. When we pass values around in a context object, with every single interactor being able to change, add or remove objects from the context object we might have a problem.

Now, if it was possible to create our own custom context class, we could decide what can be accessed or read in the context. On the other hand, if you don’t mind a global context accessed by everyone, or you don’t need one for the task you’re doing is short-lived and simple, just use the default one and you’re good to go.

Lacks a more explicit way for params validation

As explained before, there is no explicit way to validate the context object inside an interactor. The idea explained above was to use what I call a Validation Interactor. It’s a simple idea, with no dependencies and does not “stain” the philosophy behind the interactor gem whatsoever. But, as in most cases of simplicity, it may be too simple. One other solution is to go the opposite way: “pollute” the code a little bit in order to provide flexible mechanisms of context validation inside the interactor. Something along the lines of the idea of contracts, like interactor-contracts and troupe:

Conclusion

As we’ve seen, interactors solve a real programming problem, helping to avoid controller and model bloat. This pattern is inspired by the Command Pattern and provides a way to create an abstraction over our domain models and encapsulate business logic. That way, a lot of code manipulating our model objects, usually written inside the models or even in the controllers, can be moved to interactors, making our code more modular and DRY. In fact, here at Guava we prefer neither Fat Models nor Fat Controllers, but we favor the creation of multiple interactors whenever it’s reasonable to do so. And no, you don’t need a gem to start creating your own interactors. Remember: it’s a basic Service Object with one responsibility and one public method. Just keep it simple. On the other hand, if you feel like being guided by voices, try the interactors gem. It’s pretty neat. Very small code base, no magic, just straightforward Ruby. Also, very simple to plug into your service classes and start using. Try to make good use of its sharp features as hooks. But keep in mind that interactor has its cons as well. I mentioned some of them above, along with some techniques to go around them elegantly. Some of the problems, though, we’ll still have to wait for an official solution. Oh, well… pobody is nerfect.

--

--