Collective Idea

Collective Idea Logo

Jason Roelofs

Where's Your Business Logic?

By Jason Roelofs on June 28, 2012 in architecture, design, and rails

If I sat down with your code base and asked you how such-and-such a feature is implemented, what would you say? Would you lead me through controller filters and actions? What about model methods, includes, callbacks or observers? How about objects in lib/ (oh, and there’s also this other call to a mailer to make sure emails get sent out…), or would you even dive down into the database itself? Or would you send me all of these paths at once? Can you even remember or properly follow the code flow for a given use case? If any of this sounds familiar, and you’re nodding in a “yeah I know but…” fashion, but feel stuck, I’ve got an answer.

Before we continue, if you haven’t watched Uncle Bob Martin’s keynote talk Architecture: The Lost Years yet, please go do so now. I’ve yet to find a better explanation of this problem nor a better solution than what Uncle Bob presents.

The fundamental problem with almost every Rails project (and I’m sure in other frameworks as well), is that there is no direct codifying of the business rules and use cases of the application. There is no single location you can point to and say “here, these objects implement our use cases”. I put some of the blame on Rails itself, which has guided developers to use Controllers, Models, or Libraries, and nothing else. However I put most of the blame on us, the developers, for two reasons. First, we rarely spend enough time up front thinking about the problem space and designing a solution, and second, we haven’t been listening and reacting to test pain (you are doing TDD right?). Are your tests slow (>1s to run a single unit test)? Do your tests have large ungainly setup? Are you using factory_girl in your unit tests? Are you mocking implementation instead of interfaces?

If you answered ‘yes’ to any of these questions, your tests are screaming at you that your design is wrong or nonexistent. If you do TDD right, following the Red, Green Refactor cycle will lead you towards small, simple objects that do one thing and do it well. That said, getting from here to there is a very daunting task, but it’s not impossible.

Enter the Interactor. An Interactor handles a use case. It pulls together the models and libraries it needs to process a single business rule, and then it’s done. These objects are very easy to test and use and in proper OO fashion can be used anywhere the app needs to apply the use case or business rule. If an Interactor’s test ever feels painful, then the Interactor is probably doing too much and you actually have two rules being processed by one object, so refactor! Take control of your tests and your code again, and you’ll wonder why you never did this before (I sure did!).

I’m still working on the general API an Interactor should have, but this is what I currently recommend:

  • Interactor class names are Verbs: LogUserIn, ProcessComment, etc.
  • The constructor takes current-state information, e.g. the currently logged in user
  • It has one or more instance methods to fire off the process, I normally try to have one called #run.
  • These methods take any required parameterized information, like login and password from the user.
  • It can use other Interactors as needed

As an example, here’s my LogUserIn interactor from my personal project raidit that applies all five of these rules. This object takes a login type (:web, :api, :mobile, etc) and the login / password from the user, and applies the rules necessary to log the user in:

require 'securerandom'

require 'models/user'
require 'interactors/find_user'
require 'repository'

class LogUserIn

  attr_reader :login_type

  def initialize(login_type)
    @login_type = login_type
  end

  def run(login, password)
    action = FindUser.new
    user = action.by_login login
    if user && user.password == password
      user.set_login_token @login_type, new_login_token
      user
    else
      nil
    end
  end

  protected

  def new_login_token
    SecureRandom.hex(32)
  end
end

The controller action that uses this Interactor is such:

  def create
    action = LogUserIn.new :web

    if user = action.run(params[:login], params[:password])
      reset_session
      cookies[:web_session_token] = {
        value: user.login_token(:web),
        httponly: true
      }

      redirect_to root_path
    else
      flash.now[:login_error] = true
      render action: "new"
    end
  end

As you can see, the controller action takes care of everything Rails should: setting cookies, passing in parameters, clearing out the session, showing messages and redirecting. Everything else not Rails specific is handled in the Interactor. The test for this Interactor can be found here: log_user_in_test.rb and if you look at unit/test_helper.rb you’ll notice that this test doesn’t load Rails at all; it’s not needed! This gives the test an extremely fast start-up and improves the TDD experience dramatically.

So I’ll ask again, Where is your Business Logic? Do you have a nicely Object Oriented application that Rails simply uses or is your code spread everywhere as from a shotgun? If it’s the latter, Interactors are a great way to start cleaning up your code and giving your application some structure and architecture.

By Jason Roelofs on June 28, 2012 in architecture, design, and rails

27 Comments

  1. Samuel Williams

    Samuel Williams June 28, 2012 http://www.codeotaku.com

    How do you mock behaviour provided by other “Interactor” classes? It seems like it is hard coded.

  2. Jason Roelofs

    Jason Roelofs June 28, 2012 http://collectiveidea.com

    @Samuel Williams: Right now in raidit I’m taking the stance of never mocking objects I own. I know this goes against the “isolate and test” idea, but I’ve been bit pretty hard by code with too many mocks, and mocking done wrong, so I don’t mock them. As this project progresses I’ll see if this idea is manageable or if interface mocking is really needed for keeping tests clean.

    To answer your question directly, it’s easy using Ruby and mocha. In the case of LogUserIn, you’d mock with the following:

    FindUser.any_instance.expects(:by_login).with(login).returns(user)
    
  3. David Hoogeberg

    David Hoogeberg June 28, 2012 http://-

    Aren’t you simply describing the Command pattern? (Try: http://en.wikipedia.org/wiki/Command_pattern)

  4. Paul Rosania

    Paul Rosania June 28, 2012 http://paul.rosania.org

    I understand the benefit of separating concerns, but can you elaborate on why this functionality belongs in an object?  It seems like a static method (or even a free function) could accomplish the goal of coordinating interaction between your models.

  5. Jason Roelofs

    Jason Roelofs June 28, 2012 http://collectiveidea.com

    @David Hoogeberg: There are similarities yes, but this pattern is focused on being an implementation of a single application use-case, which is IMO higher level than a set of Commands that act on your domain models. The other distinction I see is that where Commands are supposed to be given everything they need to act, Interactors are supposed to be the top level access to your application (and Rails is not your application) and thus need to be able to find the information they need to function.

  6. windock

    windock June 28, 2012

    That’s nice idea, and I’m in the early stages of implementing something similar at https://github.com/windock/kobza_crm (very early stage).

    One thing to consider: there is a very decent implementation of Repository pattern at https://github.com/playlouder/persistence, at least a better starting point.

    The other is implementation of Interactors. Robert Martin has called them Transactions also in his books, where they’re indeed an implementation of Command pattern. What distinguish your Interactors from Commands (or Transactions) is lack of consistent interface: method ‘run’ takes different number of arguments. In comparison, Martin provides all required information for Transactions in constructors and setter methods. I see no reason not to provide consisten interface.

  7. Nick

    Nick June 28, 2012

    It looks to me like this is already something Eric Evans has described in Domain Driven Design with Domain Service objects (which have to be distinguished from Application Service objects.)

  8. windock

    windock June 28, 2012

    That’s interesting, how Services in DDD conflict with Martin’s Transactions:
    DDD tells: Services should not have their state
    Martin tells: Transactions are just Command objects (thus they have state).

  9. Vlad

    Vlad June 28, 2012

    Awesome, thanks Jason… another app I was digging into to get my head around Uncle Bob’s principals that your audience might find helpful is https://github.com/qertoip/guru_watch.git

  10. Jason Roelofs

    Jason Roelofs June 29, 2012 http://collectiveidea.com

    @windock: Thanks for the links, I’ll be sure to watch your CRM. As for the inconsistent API, I can definitely understand your point, particularly as the one part of of the Interactor that I’m not using is Request / Response objects, but instead passing around plain parameters and letting the Interactors return instances of my domain models. I don’t want to drop down into a pure Command object pattern with this yet, as I feel this Interactor pattern works as a higher-level version. So I’ll have to see if my current setup is maintainable or if it gets unwieldy.

    I also went ahead and ordered Clean Code, surprised I didn’t have it yet, thanks!

  11. windock

    windock June 29, 2012

    Another wonderful implementation of this idea: http://silk.codeplex.com/ .NET folks are a clever bunch, we have a lot to learn from them.

  12. Scott Burton

    Scott Burton June 30, 2012

    I’m becoming a huge fan of this pattern. It’s been tying the Ruby community in knots for the past week or so, but it’s hard to understand why it’s even controversial. 

    There are two prevalent arguments against OO at the moment:

    1. Your Rails App Is Your App – this argument is used to justify tangling business logic up with your framework. It also makes making changes a nightmare.
    2. It’s Unconventional – conventions are important, but not if they’re bad.

    Test setups tend shine a light on these two fallacies. 

    Great article. 

  13. Steve Klabnik

    Steve Klabnik June 30, 2012 http://steveklabnik.com

    Isn’t this DCI?

  14. Michael Schuerig

    Michael Schuerig June 30, 2012 http://www.schuerig.de/michael

    Yes, this appears to be low-ceremony DCI.

    Apart from any other considerations, I think the main advantage of encapsulating business logic in its own object is that it makes testing easier. In particular, you don’t have to test the logic in the context of a controller.

  15. Jason Roelofs

    Jason Roelofs June 30, 2012 http://collectiveidea.com

    Steve Klabnik</strong>: <strong>Michael Schuerig: Yes this is basically DCI, worded and presented a little differently. I’ve never been a fan of the “injecting functionality into your model” approach that a lot of DCI articles use, I’d prefer to have as much logic for a given use case be in the same object and not spread throughout modules / traits / etc.

    Either way the use case is basically the same: let another object handle the logic for implementing a given feature/business rule/use case. Describing it in these words has helped me finally understand not only what these patterns solve but how to use them to get out of this morass of super coupled code.

    None of these ideas are in any way new, however it’s not something many people in the Rails community have talked about until just recently.

  16. jjchiw

    jjchiw July 02, 2012 http://h-a-i.net/lacalabaza

    I think ayende has been doing this for a while, with Commands, Tasks and Queries as Interactor, but not everything like LogUserIn but He has a task for ProcessComment

    Here is a link of a series named “Limit your abstractions” maybe you can find it useful for your Interactor API

    http://ayende.com/blog/154081/limit-your-abstractions-you-only-get-six-to-a-dozen-in-the-entire-app

    And here is the implementation
    https://github.com/ayende/RaccoonBlog

  17. Roopesh Shenoy

    Roopesh Shenoy July 02, 2012 http://www.sqlhorror.com

    Doesn’t this fit better if you just use a first-class function rather than fitting the run() into an object? maybe a function like LogInUser()? Verbs are functions or methods, not objects. 

  18. Jason Roelofs

    Jason Roelofs July 02, 2012 http://collectiveidea.com

    @Roopesh Shenoy: I disagree. There is no hard rule nor good reason why object names can’t be verbs. If you need to do something, and you want an object and not a function, then name it “Do”. Much easier to read and understand than “SomethingDoer”. This also has the side effect of helping enforce SRP.

  19. Roopesh Shenoy

    Roopesh Shenoy July 03, 2012 http://www.sqlhorror.com

    @Jason – agreed the naming convention you have used is better than what we normally do, my point was not that it is wrong – my point was why use an object when a function will do much better?

  20. eric

    eric July 03, 2012

    Nice ideas, though your interactor class is really just a function forced into an object oriented perspective.

  21. Tony

    Tony June 30, 2012 http://semanteks.com

    Thanks for the write up. I agree that thinking about our apps this way is useful. How to you compare your pattern to DCI, and what is your experience with DCI that you’ve decided to ‘roll your own’ use-case-centric method?

  22. Paulo Köch

    Paulo Köch July 03, 2012 http://pko.ch

    @Jason @Roopesh I’m with Roopesh and eric on this one. Objects couple a bundle of data with operations on it. In these cases, I see no value in using classes over using a function.

    Also, SRP also applies to functions.

  23. Martien de Jong

    Martien de Jong July 05, 2012

    I like the idea, but is a use case not usually made up of more than just some background processing? To me it seems that the real problem is handling asynchronous calls in a workflow in an oprganized way. 
    For instance, the SignUp use-case consists of something like this:

    start when signup button is pressed:
    while !signupForm.isValid
    {
    display signupForm
    if button=cancel return
    }
    User.create(signupForm.user)
    signupForm.user.mail(signupConfirmationMail)
    Session.user = signupForm.user

    but we cannot do this because of asynchronous operations, and we have to split it up in multiple functions.
    It would definitely help to keep these together, but I think this can be done in the existing MVC structure.

  24. naming company

    naming company September 01, 2012 http://eatmywords.com/

    I am trying to use this code but I am not getting the exact pattern.

  25. jon

    jon March 02, 2013

    Check out https://github.com/cypriss/mutations

  26. James

    James April 18, 2013

    @jon You just made my day. That gem is exactly what I’ve been looking for for the past… month, at least?

  27. Toby DiPasquale

    Toby DiPasquale September 11, 2013

    Its not DCI you’re describing, its DDD. This drum has been beating for over 10 years now.

Post a Comment

Contact Us

Find us on Google Maps
Collective Idea
44 East 8th Street, Suite 410
Holland, Michigan 49423 USA 42.790334-86.105251

Follow us on the Interwebs

We are currently available for medium and long term projects. Please get in touch if we can be of service.