Hacker News new | comments | show | ask | jobs | submit login
Introducing Mutations: Putting SOA on Rails for security and maintainability (uservoice.com)
71 points by cypriss 1607 days ago | hide | past | web | 22 comments | favorite

Having transitioned from Django to Rails nearly a year ago, this post reminded me of django forms[1]. When I used django, I didn't think much about them, but moving to Rails, I felt something was missing. Why do validations live only/primarily on the model? Doesn't it make more sense to do validation higher-up the chain to filter mistakes and potentially harmful input?

Also a good point about the `attr_accessible` concept. It always felt like a bit of a crippled way to perform authorization. That said, I'm not sure this comment is completely valid:

> attr_accessible suffers from context blindness: you’re frequently going to have an end user UI and an admin UI. You want admins to have access to more fields.

Whilst it's not the most elegant, you can (and should) define `attr_accessible :x, :y, :z, :as => :admin`

but you have to remember to use something like `MyObj.create({x: 'a', z: 'b'}, :as => :admin)`

[1] https://docs.djangoproject.com/en/1.5/topics/forms/

You're right about the attr_accessible statement not being entirely accurate. Rails 3 does indeed have the concept of contexts. And, we're still on Rails 2.3 :). It also seems like Rails 4 will being going Strong Attribute route.

I really like this approach. I use something similar for my work, however this has a much nicer API.

Often these patterns are treated and discussed as an either/or decision, which I don't agree with. There are many places in an app where the default and simple Rails way is the right choice. However as apps get more complex, there is also a need for approaches like the one presented here.

My rule for choosing the right approach is this:

If it's just a simple CRUD controller where it receives input from a simple form and writes attributes to a single model, with no AR callbacks involved, then omakase is perfectly acceptable. In that case I don't want to have to jump through all kinds of hoops and overcome unnecessary friction added by enterprisey patterns.

However, as soon as I have to use AR callbacks, add more lines to my controller action, or touch more than one model, that's a clear indication that a service object makes sense.

Mutations looks like a great solution for the latter case. It brings in some DCI goodness as well. The key is that some operations require complex context (object graphs, permissions, dependent operations), and that context should be treated as a first class citizen.

I'm a big fan of this pattern. We use a smaller contextual form process that includes ActiveModel::Validations and provides some simple defining methods, and does a very similar thing. Since most services / mutations are generally bound to a form, it's just called BaseForm, and its children are LoginForm, TweetForm etc.

I'm glad to see rails devs are finally coming to their senses and exploring other patterns for building applications. SOA, command objects, etc. have been a pretty common pattern in "enterprise-y" frameworks for awhile. This is making rails look more like grails, which I think is a good thing.

It's not new in Rails apps actually; it's only new if you've been reading vocal blog posts (including the ones a few years back bashing design patterns etc).

At least for the apps I've been maintaining as a freelance, quite often they had services of some kind and other classic patterns.

Many Rails developers I know come from a Java and/or .Net background, so this is not really a surprise :-)

Is there a reason why it was not created with multiple commands / methods in mind? :

  class UserService
  service :signup do |config|
    config.required do end
    config.optional do end
        # signup stuff here
  service :delete_account do |config|
    config.required do end
    config.optional do end
        # signup stuff here

This would also make it possible to do service description and even expose a json or xml api using just this.

But I guess the controllers are created for this. DHH says controllers are responsible for mailing and stuff, which is more correct than in the models with callbacks.

It seems like what you've done there is create two mutations (signup and delete_account) within a single namespace. You can certainly do that with the Mutations gem.

What I think you're getting at is: can you just replace controllers with mutations? That's an interesting concept that I've thought a bit about. In practice, we have found controllers to still be useful in collecting some information and doing 'wiring'. They're also nice because your SOA shouldn't really be concerned with the presentation of the response.

Finally, where do you put your mailings? You're right that controllers > models, but I'd also say that mutations > controllers > models.

This seems like a great idea! I have a few thoughts that comes to mind, perhaps others can chip in:

- How can this be used in tandem with strong_parameters; should ensuring the structure of params be a responsibility of the controller or the mutation (I'm leaning towards the former)? [1]

- How can validation duplication between models and mutations be avoided? In the example 'string :email, matches: EMAIL_REGEX' was used, but presumably one will still validate the email in the model. I'm guessing there are solutions to this, perhaps the existing model validation can somehow be used?

[1]: https://github.com/rails/strong_parameters

We're using this in Rails 2.3 without Strong Parameters. Mutations whitelist input just like strong parameters do. I don't really see why you would use both, although you certainly could.

The duplicate of validations between models and mutations is interesting. Currently at UV, we have some duplication -- however, in other cases we're relying entirely on the validations of the Mutation. I do think it's possible to rely entirely on validation from mutations -- I need to get some more real-world experience with this to know how well it will work out :)

You could probably write something like:


Similar to that query project.. Can't remember the name now :-)

How would Mutations compare with Concerns, which seems to be DHH's preferred method?

One big difference would be encapsulation.

With mixins (sometimes called Concerns), you have a shared state between the module and the class including it.

With a real class, you have your own space and (probably) don't have to deal with state and method collisions.

Pros/cons to each surely, and a lot is a matter of taste at that.

I don't see the correlation here

What are other approaches/patterns to more SOA to manage more complex code bases with Rails?

Looks like typed function to me.

That's exactly the analogy I was going for. You don't want your general ruby code to be typed-checked, but it's actually fairly important to make sure your user inputs are checked, especially until Rails battens down its hatches.

I heard DHH hates SOA, but the only reference I can find to this is an old interview from 2006. Anyone have a link to anything recent?

Web site breaks page zoom. Argh.

I almost fell off my chair laughing when I saw "Rails" and "Security" and "Maintainability" in the same sentence.

A quick search here itself on HN will show you how immature and security-hole ridden Rails is. Built for, and by, HIPSTERS!

Really? Did you read the article? It argues that using this pattern can reduce security risks because conformity of input data is more thoroughly tested before passing off to ActiveRecord.

Stupid hipsters with their stupid bicycles.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact