1. 11
  1.  

  2. 15

    DHH’s approaches and code, for better or worse have been shoehorned into many diffrent applications and frameworks. While in some (most?) applications they don’t necessarily cause any major issues I would urge any person/project/team considering this or any architectural / technological choice to introspectively choose to do these things over cargo culting from other sources. While I don’t think that this is a bad idea, at the end of the day all you are doing is hiding crimes in a different location of your application. Personally before adopting this approach I would need to find answers to:

    • Why should I do this over trailblazer?
    • Why should I do this over service objects?
    • Can I cleanly map whatever task I’ve been given to this?
    • Does this make sense with the rest of the application / ecosystem?
    • If this was the wrong choice how hard will it be to move away from it?
    1. 2

      It’s worth recording and remembering the trajectory of engineering decisions advocated by the Rails community:

      • 2008: “Rails is so simple! You don’t need all that software engineering BS!”
      • 2010: “Why are our tests so slow? We need slim models! Our controllers are getting complex and complicated, help!”
      • 2013: “SRP/service objects are really cool for fat controllers!”

      What galls me: no hint of irony of “discovering” existing, well-known software engineering solutions.

      How many will fall for this schtick the next time it makes rounds in industry?

      1. 1

        My experiences have not led me to share your dissatisfaction.

        In conversations I have had with folks working on Rails applications, I often hear acknowledgement that these ideas aren’t new. In fact, it’s often the best part of the conversation. A recent occasion led to a discussion around which parts of Martin Fowler’s Patterns of Enterprise Application Architecture are a good fit to address some of the sins of framework-driven architecture (esp. Rails), and which are not.

        Perhaps at conferences or in the blogosphere, where people take the opportunity to stand out (“Look at me, I did [thing]!”) it is more common? Even there, I have seen ideas introduced as being fresh/new, insofar as awareness of them among the Ruby ecosystem, but which also mention “Some of you might recognize this pattern from Java.”

        A specific example is a Ruby conference talk about controlling 3rd-party dependencies. I’ll see if I can find a link, but I remember the presenter brought up DTOs and said something to the effect of “They’re nothing new or revolutionary.”

        1. 1

          Funny you should mention it - Rails is based on DHH’s reading of PoEAA. If you click around Fowler’s online summary you’ll see that all the patterns that appear in Rails had their diagrams redrawn by DHH.

          The nice diagrams were redrawn for me by David Heinemeier Hansson

      2. 2

        …at the end of the day all you are doing is hiding crimes in a different location of your application.

        This gets to the heart of it. If your business logic diverges from what Rails cleanly provides, then it is okay that Rails has no cut-and-dry answer for where to put your code. The solution is to be comfortable implementing one’s own layer, metaphor or pattern as needed [0].

        Looking at the article, the trouble with the author’s Api::V1::PurchasesController#create example is the high likelihood of load_product, load_device, load_or_create_user occurring elsewhere, and the barriers to code reuse that occur when you keep that logic as methods in a controller.

        For instance, I use service objects [1] extensively in a mature, medium-sized Rails app. If I simply copied over that imperative sequence (and its supporting methods) into a service named CreatesPurchase, it causes has the same sort of trouble, just in a different place. The real goal is isolation. Isolation of the responsibilities implicit in methods with names like authorize_payment.

        To achieve that, I decompose those services into objects with other responsibilities that live in app/queries, app/permissions, app/policies and so forth. Each denotes a specific category of responsibility, and follow similar patterns of reuse. In this way, the service objects are always imperative orchestrators, and the sub-steps of those workflows are isolated and composable.

        [0] Or, as mattgreenrocks notes, borrowing patterns from the smart folks that came before you.

        [1] I dislike the name “service object” and instead call them “interactors”. I relent, however, that the name “service object” has better recognition among Rubyists.

        1. 1

          That’s a really useful pattern, thank you for your detailed explanation.

          In the specific case of versioned APIs where more than one version is live for more than a couple weeks, I also like to put all those things in app/api/v1/{controllers,queries,permissions,etc.}. I’ll just straight-up cp -a app/v1 app/v2 when it’s time for a new version. I’m taking the annoyance of having more code where the team has to duplicate bug fixes to avoid the risk of someone forgetting a subtlety of how the old API needs to work when making feature changes.

          1. 1

            If the old one version needs to retain its functionality independent of the new version, then a straight copy seems like a better answer than trying to smash two possibly-conflicting versions into the same {query,permission,policy} object. I’m reminded of that Sandi Metz quote:

            “Duplication is far cheaper than the wrong abstraction”

      3. 7

        The post reminded me of Steve Yegge’s Execution in the Kingdom of Nouns. But mostly I posted it because I was pleasantly surprised to see something I agreed with DHH on; every Rails app I’ve seen as a consultant that’s not strict about inventing new nouns for controllers and only using the basic verbs turns into a complete mess.