
Rails – The Missing Parts - tomblomfield
http://eng.joingrouper.com/blog/2014/03/03/rails-the-missing-parts-interactors
======
dhh
The proof is always in the pudding. While there are good and reasonable times
to introduce "interactors", this particular example is poor. The tests
presented are anemic, and the code is absolutely not any clearer by being
extracted and wrapped. I would indeed have kept all this in the controller.

The key point for an "interactor" extraction is imo when you have multiple
models being created in symphony, like a Signup model. Or if you for some
reason need to reuse the behavior.

But if all your controllers look like this, with one "interactor" model per
action, you're doing it wrong.

Whatever floats your boat, though. If this is what you prefer, great. But
please hold the "beginner's version" crap. Plenty of large apps are built with
vanilla Rails. Basecamp is one.

~~~
dhh
I rewrote the code in this example to use the "Beginner's Version" of Rails (
_sigh_ ). You judge which you like better:
[https://gist.github.com/dhh/9333694](https://gist.github.com/dhh/9333694)

~~~
dhh
Here's another version that doesn't even use private methods in the controller
and uses a PORO for the email grouping:
[https://gist.github.com/dhh/9333991](https://gist.github.com/dhh/9333991)

~~~
iamwil
I can't seem to find it on Google. What's a PORO?

~~~
cheshire137
Plain Old Ruby Object, as opposed to an ActiveRecord model.

------
ollysb
Perhaps because it's written with examples in java but I often feel like no
one in the rails community has ever read Eric Evan's Domain Driven Design[1].
It's far and away the best material I've ever seen on how to organise large
code bases. It covers pretty much every suggestion that I've seen from the
rails community. Sometimes the rails community can feel like the fitness
industry, everybody just rebranding things that have been done before.

[1] [http://www.amazon.com/Domain-Driven-Design-Tackling-
Complexi...](http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-
Software/dp/0321125215)

~~~
tomblomfield
Sure - I don't think anyone in the Rails world is claiming to have invented
these principles.

The problem is that Rails ships with a very limited set of core architectural
concepts, and many inexperienced Rails developers feel like they've got to
cram all of their code into a Model, View or Controller.

Once your codebase reaches a certain complexity, principles from other
programming paradigms are extremely useful.

~~~
rubiquity
> "The problem is that Rails ships with a very limited set of core
> architectural concepts, and many inexperienced Rails developers feel like
> they've got to cram all of their code into a Model, View or Controller."

The problem is that people think Rails is an architecture to begin with. Rails
is just a framework that uses the MVC pattern (mangled slightly to fit the
realm of HTTP). In the end, MVC is nothing but a directory structure for our
files. What you put in those files and directories is up to you. Uncle Bob
gave a great keynote on this called Architecture: The Lost Years. Here's a
link:
[http://www.confreaks.com/videos/759-rubymidwest2011-keynote-...](http://www.confreaks.com/videos/759-rubymidwest2011-keynote-
architecture-the-lost-years)

~~~
mattgreenrocks
You have to realize: Rails is incredibly populist in that it's "good enough"
architecture for many devs. It's not terrible, but it's not at all the same as
learning basic architectural principles for building apps. Devs don't stray
outside of it much, they just deal with it when it gets to be a problem.

In this way, we've successfully commoditized another differentiating factor of
developers so we can ship on Internet Time(tm).

------
programminggeek
I've spent more time thinking about clean architecture and design than most
and the conclusion that I've come to is while Ruby gives you all the tools to
write clean code and great architecture, it doesn't offer the best tools to do
that job.

Ruby and Rails feel best as a prototyping platform for getting something out
the door fast, proving a concept, and not worrying so much about correctness
or maintenance.

I don't think if you are doing a lot of TDD and larger systems that piling on
more Ruby and Rails is the right answer. I think once you know what you are
working with, a well designed language with a compiler is a huge help and
would remove a ton of useless tests and stuff that you end up writing in Ruby
by hand.

This very likely leads to an API driven system with an API written in a
strongly typed, compiled language like C#, Java, Scala, Haskell, or Go and
writing your front end in whatever makes the most sense for your team.

At that point you get the benefits of a nice rapid development platform like
Rails for your front end, and a fast, testable, API written in something else
using all the clean code architecture you want.

The trick is, you do everything ugly in Rails or PHP or whatever in your
initial prototype and you might not even write tests. You just ship working
code until your prototype has proven a business case. Then, you move it
towards something with lower TCO over time. Make the investment in your code
when it makes sense to invest in it and use the best tool for the job at each
step.

You probably never need to leave the standard Rails MVC stuff on most projects
unless they are wildly successful and/your long term needs change. Even then,
you can probably keep the rails front end stuff and just talk to an API and be
very happy.

------
rjspotter
I agree completely that the Interactor pattern makes for cleaner Models and
Controllers in a Rails codebase. I've used both the Interactor gem and PORO
(in a directory outside of /app or /lib).

Having worked with the Interactor gem for a little while once you break things
down into small interactors that can be used by the Organizers, I have two
main complaints.

1) inputs are unclear. With calling new or some custom class method you can
use descriptive variable names to say what the object expects to be present to
do it's job. With the Interactor gem you end up adding in comments describing
what keys need be present in the context and what keys will be added to the
context so the next programmer can use the interactor you've created without
having to go through and grok everything.

2) You end up having to (re)create a failure protocol to communicate with the
controller and display to the user. We take the AR errors functionality for
granted in our controllers/views with interactors you have to come up with a
similar system.

2.5) as a result you end up writing a lot of boilerplate fail! and rollback
code

2.5.2) and a non-atomic operation like notifying the payment gateway can break
the whole model of rolling back and you have to end up raising so your user
doesn't end up in a invalid state or get charged twice.

~~~
tomblomfield
Agreed, especially on (1). I don't love the lack of explicit inputs.

We've got a convention that all Interactors must be commented to specify what
inputs they take.

------
casey_lang
Along the same line but heavier weight than 'Interactor' is
'ActiveInteraction'[1].

Responds in much the same way active record does, allowing validations,
errors, forms built from interaction objects, etc.

[1]
[http://github.com/orgsync/active_interaction](http://github.com/orgsync/active_interaction)

------
tzaman
We tried using DHH's concerns in place for interactors, but ditched them for
PORO/service objects because they can be tested outside Rails.

~~~
tomblomfield
Yeah - I feel like concerns are a bit of an anti-pattern, especially as
they're normally used in Rails.

You've got a God class that exposes 500+ public methods, so you split it into
concerns. But now you've still got a God class with 500 methods that's split
across 10 files. Good luck understanding that.

Concerns are useful when they're genuinely sharing functionality between
classes - not just for splitting up "Big Bags O' Methods".

~~~
epidemian
> Concerns are useful when they're genuinely sharing functionality between
> classes - not just for splitting up "Big Bags O' Methods".

Hmmm. I'd say it depends.

Of course having concerns (a fancy word for modules IMHO) shared between
classes is great. But i've also found cases where separating a god class'
behaviour into different concerns helped a lot. The important thing in those
cases was recognizing and avoiding dependencies between different concerns, so
instead of ending up with a god class split across many files of inter-
dependant code (i.e. worse than before), you can end up with a "god" class
split into its minimal "core" part and a couple of concerns that only depend
on that core part but not between each other. Yes, you could still call that a
god class, as it will have a very fat public API, but at least you can reason
about what it does in terms of different concerns, so you gain something :)

------
pothibo
One thing I really enjoy with this post is the conclusion. It doesn't try to
tell you it's the only way, just that it's the way they found to fix their
problem with the constraints they had, as it should be.

Rails is easy to extend, people often forget that. Great post.

------
edwinvlieg
We've also come across the limitations of Rails as a one-size-fits-all
solution for larger codebases. I don't think this is a fault of Rails, but
more of the developers using the framework. Software engineering in general
has more focus on a wide variety of design patterns and doesn't limit itself
to one framework. In my opinion, Rails is the best framework to create
wonderful web applications. Modeling business logic requires different kind of
architectures than the simple MVC-ish structure Rails provides.

At MoneyBird we are using the Mutations gem to represent something like the
interactors mentioned in this article. One major advantages of Mutations, is
that is also does input filtering.

[https://github.com/cypriss/mutations](https://github.com/cypriss/mutations)

------
Axsuul
I've been looking into incorporating this pattern into my larger Rails apps as
well. Another benefits of interactions is DRYing up your code for use in APIs.
Some of the more popular interactor gems:

[https://github.com/orgsync/active_interaction](https://github.com/orgsync/active_interaction)
[https://github.com/cypriss/mutations](https://github.com/cypriss/mutations)

~~~
rubiquity
If you need a gem to implement some of these complementary patterns, you're
doing it wrong. Draper, Naught, DisplayCase and all of the other gems of
patterns are over abstraction 99% of the time. Implement the pattern yourself.
Most of these Presenter and Service object patterns are different variations
of the Decorator pattern. Ruby happens to ship with 3 great ways to implement
decorators: SimpleDelegator (my personal favorite)[0], Delegator[1] and
Forwardable[2].

0 - [http://www.ruby-
doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/Si...](http://www.ruby-
doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/SimpleDelegator.html)

1 - [http://www.ruby-
doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/De...](http://www.ruby-
doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/Delegator.html)

2 - [http://www.ruby-
doc.org/stdlib-2.0/libdoc/forwardable/rdoc/F...](http://www.ruby-
doc.org/stdlib-2.0/libdoc/forwardable/rdoc/Forwardable.html)

~~~
ryanbrunner
The biggest pain points that gems like Draper solve are allowing view helpers
in your decorators (which is by no means straightforward when you get into
routing helpers), and dealing with some of the strangeness that you get into
by wrapping ActiveRecord models (JSON serialization was a biggie for us). It's
less about what Ruby gives you, and more about navigating around some of the
darker, messier corners of Rails.

~~~
rubiquity
They just wrapped the view_context with a method_missing proxy (another form
of a Decorator) that the controller instance gives you. You can accomplish the
same very easily using dependency injection in your controller action:

MassivelyUpVotedPostPresenter.new(@post, self.view_context)

Or just inject the whole controller instance and you have your router helpers
too!

MassivelyUpVotedPostPresenter.new(@post, self)

------
nwienert
I've found a happy balance developing with rails by adding two strategies:

1) Use presenters for display-only logic to keep controllers concerned with
managing requests only.

2) Using service object / custom classes in /lib for actions on models as well
as abstracting any common related functionality. Creators, Updaters,
Processors, Orchestrators, etc. Keep your models only concerned with data and
not data transformation, and your controllers from doing it as well.

~~~
jhund
I have documented my strategy for moving from a simple MVP to a maintainable
and complex Rails app. Basically I look at factors like how many objects are
affected by an operation, and how complex the additional processing is. TL;DR:
Use Rails magic when possible, and Object Oriented best practices when
necessary.

I created a flowchart that illustrates this strategy: [http://rails-
recipes.clearcove.ca/pages/how_to_change_object...](http://rails-
recipes.clearcove.ca/pages/how_to_change_objects.html)

------
wdewind
This is great, and does not only apply to Rails. In many other frameworks it's
common to lump business logic in controllers instead of models. Either way you
end up with the same thing: eventually you'll need to add a 4th (at least)
type on to MVC.

> Here at Grouper, we’re long-time users of Ruby on Rails – along with other
> New York startups like RapGenius, Etsy and Kickstarter.

Etsy doesn't use Rails though, it uses PHP.

~~~
tomblomfield
Fixed, thanks

------
kapilkale
Can anyone recommend a github repo of a Rails app implementing interactors or
any of the similar concepts listed in the comments?

~~~
ahawkins
I humbly submit my own work: [http://hawkins.io/2014/01/rediscovering-the-joy-
of-design/](http://hawkins.io/2014/01/rediscovering-the-joy-of-design/)

------
sdegutis
How about just "features"?

Maybe there are some "user" features (login, logout, change name) and "cart"
features (add/remove item in cart).

These features would all live horizontally, be able to call each other (design
your object graph wisely!) and would never be able to inspect or elaborate on
implementation details!

These features may or may not talk to some database for its own persistence,
but that's up to the functions.

And the features are pure functions in your language of choice. They have no
knowledge of the web, or requests, they're perfectly transient. They act on a
"state" object that you pass around to them, which in tests is just a blob of
memory and in production is a real database or wherever else you store state.

It's simple, yet so powerful. This is how we do it, and it's scaling very
nicely.

------
avitzurel
As always, the question is should be in Rails or not.

I think that the answer is clear, this should not be a part of rails since
it's not true to all apps.

I think that if you want to have something real quick, you don't need an
interactor/service class.

When you have a bigger app, you definitely need that, or you will get to a
point where you code is split into models/observers/callbacks/lib/app/concerns
and you can't find anything.

Rails generators are pretty easy to extend, this way, when you generate a new
model, you can easily create the service class for it.

You can also EASILY create new generators that will generate the service
classes for you with your defaults and templates and what not.

Not sure Rails is missing that, however, it is definitely a best practice that
people with bigger apps should use TODAY.

~~~
ryanbrunner
Mailers, concerns, helpers, observers and a lot of other things in Rails
aren't universally used by all apps. Even something as simple as an empty
directory and some generators in a stock Rails app would go a long way in
educating people that these are patterns that you can use / provide a common
language for where these sorts of things should go and what we should call
them.

------
iagooar
I recommend this article from the guys from Code Climate, it is really mind-
opening: [http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-
decomp...](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-
fat-activerecord-models/)

The author describes different ways of refactoring fat ActiveRecord models,
but most of the ideas can be used outside ActiveRecord, as "best practices".

I have been using some of the patterns described in it and I am really happy
with the results. The code becomes more clearly decoupled, is easy to test and
results in clean, slim models.

------
evilgeenius1
When reading DHH's comments it's important to remember that the domain model
for Basecamp is extremely simple. It's a brilliant piece of software that's
been well thought out, but the underlying domain concepts are extremely basic.

I wonder if he's ever worked with an app that has a complicated domain.

His comments taken from this perspective make a lot of sense. Rails was
written for Basecamp.

For large apps with real complexity, the rails way falls short.

------
tuke
"The nail in the coffin is ActiveRecord callbacks; before_save hooks on one
class that modify the state of other objects."

before_save _can_ modify the state of other objects. It doesn't have to. One
might use it to modify only the state of the current model. You are reaching a
bit, blaming the callback, when what you are really concerned with is an
awkward and potentially dangerous use of the callback.

------
GutenYe
What's the different between interactor and wisper? Wisper is better doing the
job in your example.

interactor:
[https://github.com/collectiveidea/interactor](https://github.com/collectiveidea/interactor)
wisper:
[https://github.com/krisleech/wisper](https://github.com/krisleech/wisper)

------
seancoleman
A good litmus test of an experienced Rails developer is how large their /lib
directories are in relation to project sizes.

~~~
nimblegorilla
Is a large /lib a sign of experience or inexperience? I try to keep as much as
possible out of /lib and moved into gems.

~~~
awj
Inexperience. lib/ quickly turns into a dumping ground unless you are careful
to keep it clear of business logic.

------
reedlaw
I wrote a blog post along similar lines [1]. The benefits are clear to me:
nearly 200 tests run in about a second. For that reason alone I wouldn't
consider coupling domain logic with ActiveRecord.

1\. [http://www.smashingboxes.com/domain-logic-in-
rails/](http://www.smashingboxes.com/domain-logic-in-rails/)

------
troels
One thing I can't figure out is whether to place my service objects under
`/app/models` or under `/lib`. There doesn't seem to be a clear consensus on
this? I tend more towards the former, because autoload works better during
development. Also, I consider them part of my domain model. What do you do?

~~~
Legion

      /app/interactors
    

It's a trivial thing to add new folders like this to autoloading:

    
    
      # add to application.rb
      config.autoload_paths += %W(#{config.root}/app/interactors)

~~~
nthj
As of Rails 3.0, I believe app/anything/ is autoloaded:

* [https://github.com/rails/rails/blob/3-0-stable/railties/test...](https://github.com/rails/rails/blob/3-0-stable/railties/test/application/initializers/load_path_test.rb#L43)

* [http://hakunin.com/rails3-load-paths](http://hakunin.com/rails3-load-paths)

------
andrzejkrzywda
If anyone's interested in a more real-life example, then this post presents a
Rails controller refactoring from the Redmine project.

[http://blog.arkency.com/2014/02/rails-refactoring-the-aha-
mo...](http://blog.arkency.com/2014/02/rails-refactoring-the-aha-moments/)

------
csense
The first missing part: A language with a sane syntax. For some examples of
the multitude of ways in which Ruby syntax is awful, see [1].

The second missing part: Ruby is Rails. Trying to learn Ruby _at the same
time_ as a modern web framework with all its moving parts? Forget it.

The third missing part: Tutorials. Okay, I haven't done a rant on this. Google
rails tutorial. The first helpful result I get [2], I scroll down. The first
meat is "Setting the application home page". I see this:

    
    
        Blog::Application.routes.draw do
          get "welcome/index"
    

Huh? "This is your application's routing file which holds entries in a special
DSL (domain-specific language) that tells Rails how to connect incoming
requests to controllers and actions." So it's not even Ruby or some recognized
Web glue language like HTML or CSS; it's some domain-specific language? Okay,
I totally don't get it, so as suggested, for more details, I should refer to
"Rails Routing from the Outside In" [3].

This is even more confusing. It says you should do something like this:

    
    
        get '/patients/:id', to: 'patients#show'
    

Huh? What's the deal with the colons and octothorpe? I can guess colon is the
signifier for an ID in the URL, but why the pound sign for #show?

    
    
        get '/patients/:id', to: 'patients#show', as: 'patient'
    

And what's the deal with the colons?

I could go on and on. I'm sure that with a few hours of pain and frustration,
I would be able to figure out exactly all the oddities of Ruby syntax, or the
template language, or the DSL whatever, and understand this example well
enough to extend it.

But that's not the point. The point is that, because I have to spend those
hours, it means that Ruby/Rails is poorly designed. In Django, by contrast,
things are almost always simple and obvious.

[1]
[https://news.ycombinator.com/item?id=5872899](https://news.ycombinator.com/item?id=5872899)

[2]
[http://guides.rubyonrails.org/getting_started.html](http://guides.rubyonrails.org/getting_started.html)

[3]
[http://guides.rubyonrails.org/routing.html](http://guides.rubyonrails.org/routing.html)

~~~
dablweb
Ruby has a sane syntax. It probably seems a little foreign if you're new.

'#' is the standard notation used throughout the Ruby language for instance
methods.

Think of it like this:

    
    
      Blog::Application.routes.draw do |implicit_self|
        implicit_self.get( '/patients/:id', { to: 'patients#show', as: 'patient' } )
      end

------
dablweb
I like the interactor gem but don't understand why the method activating it is
"perform".

Why isn't it "call" so it can be swapped out with a lambda, or method
reference without hassle?

~~~
dablweb
As in this article. [http://blog.arkency.com/2014/02/rails-refactoring-the-
aha-mo...](http://blog.arkency.com/2014/02/rails-refactoring-the-aha-moments/)

Services should be run with call.

------
ksec
It sounds similar to what Lotus is trying to do.

[http://lotusrb.org/](http://lotusrb.org/)

~~~
jbeja
How do you even know about this, so unknown, Thanks :).

------
clutchski
I thought Etsy uses PHP. Could be wrong.

~~~
SpikeGronim
I can confirm that Etsy.com is primarily PHP. We have no externally facing
Rails projects. We have some internal tools on Rails.

\-- spike@etsy.com

~~~
tomblomfield
Sorry, fixed!

------
ream88
I would love to take a glimpse on the Basecamp code, dhh ;)

