Hacker News new | past | comments | ask | show | jobs | submit login
Where the logic hides in rails apps (gammons.github.com)
66 points by dogas on Dec 23, 2012 | hide | past | favorite | 55 comments



I'll give a proposal now: every chair of every developer shall from now on give them electric shock whenever they use the term "business logic". Ouch.

Seriously, "CreatesContact" is not really a class. It's a procedure, function with side effects, whatever you call it. Just with a class wrapper that boosts the developer ego almost as much as an AbstractFactoryManagerFactory.

No, modules are actually great. If you have billions of tiny modules all over the place, you probably (note the "probably") optimized way too early by splitting things out into them without a clear reason.

And guess what, if a new developer comes into a project and investigate stuff, they will see the model class start with all sorts of metadata, including callbacks, including those fancy ones called "before_save". If they can't guess what "before_save" means, you might want to either pay for an English class, or switch the framework to something that speaks in German.

And as for using rails as a web delivery mechanism, can you please, please, please investigate things like Sinatra and plain old rack, because those are delivery mechanisms. Rails is a framework, and it's entire point is being opinionated. If you disagree with the opinions, you probably should investigate other frameworks, or just use the libraries that you like (including those that are parts of Rails). Or maybe whack Rails into whatever shape you would like you to be in.

(Also, monolithic applications happen to the best of us, but the problems in them actually often come from the fact that they're monolithic. Stop looking for other developers to blame).


So... The department says if the sales recept is over $10,000, the system must present the user with another form that must then be approved by two managers before the request is continued. Where does that code live, and what do you call it?

I call it business logic.


A class that has a name that's not a noun should always be suspect, imo.


> give them electric shock

even better, force them to use COmmon Business Oriented Language


They should also use principles from the Object Management Group.


and after they finish building the system with separated business logic, suggest their business client to add a formal business requirement that all data should be persistently stored. just in case.


This is the sanest Rails design-advice that I've read in a long time.

It counters the misguided "implicit over explicit"-mantra that is both very prevalent in the rails-community and also the source of most problems.

I.e. when you watch any random RailsCast it is usually filled to the rim with obscure incantations and "look how we need only one LoC to perform $excessive_magic"!

The end-result are those deeply entangled "piles of rails" that we've all seen and suffered from. Hopelessly overloaded models and a dense mesh of hidden interdependencies that nobody grasps anymore because many of them are not even explicitly declared.

Personally I've largely given up hope on rails and am waiting for the successor. The rails-team just seems too fixated on digging their rabbit hole ever deeper, rather than re-visiting design mistakes that were made early on.

However, if you are stuck with a Rails-app for the time being (and who isn't..) I'd definitely recommend to follow a pattern like the one outlined in this post. The best way to use Rails nowadays is to steer clear from most of the entrenched practices and packages (e.g. devise and related trainwrecks) and to use it like a library rather than a framework - as much as that is possible.

This eases the migration to the rails-successor when it manifests, and helps preserve the sanity of your older self and his successor.


I agree, although I'd add that the "magic" can be both the best and the worst part of Rails.

The "magic" is what makes Rails extremely fast to prototype with. For me, at least, it's what makes working with Rails fun, since it lets you spend time writing application code instead of repetitive business logic.

It's also what leads a less-experienced developer to get in over their head quite quickly. My only problem with programs like Dev Bootcamp is that complete newbies shouldn't be learning Rails, they should first be building an application with a much-less robust framework so that they truly appreciate (and are wary of) how much "magic" is in a framework like Rails.

As a side note, if you haven't done so already, take a look at express for node.js. You say you're "waiting for the successor", and it seems like you want something less all-encompassing than Rails.


steer clear from most of the entrenched practices and packages (e.g. devise and related trainwrecks)

I agree with your sentiment in general, but what do you suggest to use in place of devise? Surely you're not suggesting to write all the code for handling mail confirmation, password changes etc. yourself?


In fact for serious apps that are expected to grow I do recommend to write the auth yourself. It's not a lot of code, from the second time it's mostly copy/paste, and most importantly you'll fully understand what your code does and when, there will be no guesswork in one of the most important areas of your app.

Furthermore you usually end up heavily customizing whatever auth-code you start with anyway. The shrinkwrapped gems never cover even half of what an app eventually needs. Thus another advantage is that you won't have to reverse engineer devise or authlogic when you arrive at that point (and trust me, you really don't want to look at their code and the SQL they emit).

That all said, in a recent project I've found the 'sorcery'-gem to provide a reasonable baseline for rolling your own. It's one of the first "second generation" Rails-gems that refrain from most of the magic and instead provide a set of primitives that you wire up yourself.

It still suffers from a bit of rails-smell (code-generators...), but overall the level of abstraction looks about right.


So you're one of those guys. I inherited a Rails app that used roll-your-own auth instead of Devise or similar — it added a significant amount of mental overhead, plus a bunch of additional logic for Facebook login.

Task that with another product I just launched: over 100K users, on Devise, no customization needed, and Login with Facebook took 1 additional library, 3 lines of code and 15 minutes.

I used to be the roll-your-own-everything guy, years ago. After maintaining dozens and dozens of Rails apps, I'm not anymore. I'm much happier when logic is pushed into libraries and I can ignore it until it matters.

> In fact for serious apps that are expected to grow I do recommend to write the auth yourself.

No, no, no, just no. If you don't grow, you just wasted time you should have spent shipping real features. If you do grow, refactor in your own authentication later if you have to.

> you'll fully understand what your code does and when

You will, for sure. When you leave for another project and I'm brought into maintain $app, I won't, straight away.

> there will be no guesswork in one of the most important areas of your app.

There is no guesswork with 3rd-party libraries. `bundle open gem-name` is your friend. If you read through the code, you'll understand what it's doing, just like I'll have to read through your code if you roll-your-own library.

One of the major benefits to standardized libraries is that I only have to read the library for Devise or AuthLogic once across a dozen apps. I have to learn each customized solution 100% of the time.

> It still suffers from a bit of rails-smell (code-generators...)

I get the feeling that you work on really large apps with a lot of custom logic, and end up wishing you had complete control over everything. In such situations I'd probably end up agreeing with you 90% of the time if you suggested throwing out a 3rd-party library and rolling your own.

I just strongly disagree that you should start out that way. Your app should have a single selling point: "Schedule my tweets", "Remind me to pay bills", "keep track of my tasks", or whatever. Not "Log in with our unique authentication code."

No app ever became a million dollar company because a developer thought the most important job was to roll their own authentication scheme before they even scored a thousand users.


I think our opinions are not as far apart as it seems.

You are of course right that rolling your own auth won't be the first priority in your initial PoC. Neither is it needed for very simple apps or when you're dead-certain that you won't need more flexibility than the common gems provide.

However in my experience the latter almost never applies in a commercial app. Suddenly you need OmniAuth in addition to devise, and some form of ACLs. Then you grow an API that also needs some sort of auth-tokens. Then there's this other site you want to interface with which needs yet another bridge. Then one day you run that ad on TV and learn the hard way that those extra-lookups devise makes on every request are not free after all...

So what I'm saying is that the design of (in particular) devise and authlogic is just not a very good one to start from if you can already predict that you'll need customizations (beyond templating) in the future.

A frankensteined devise can be a lot harder to understand than a straightforward impl from scratch - but in the end it of course also boils down to who wrote it and whether he wrote it for the first time.


I like Sorcery. https://github.com/NoamB/sorcery It calls itself magical, but it is very easy to wrap your head around and override default behaviors if needed.


Check Authlogic out, it is clean, easy to grasp sourcecode, maybe the closest to implementing it all yourself in the simplest way posible:

  https://github.com/binarylogic/authlogic


I think Rails developers made some deal with the devil where every line of code they write makes them more his dark Servant in exchange for the ability to write Ruby code. (They rarely know any others. Shoulda negotiated for some Erlang.)

Thereby, they seek to write as few lines of code as possible, at the expense of the sanity of everyone else.

Furthering the goals of the dark one all along...


Maybe this is naive, but what's wrong with just putting `UserMailer.welcome_email(user).deliver` in the controller, right after the user is created? In my mind, delegating an email to be sent should be a job for the controller.

IIRC, the Rails ideology says that "Models should not know about any part of the application except for their own datastore." Even though the :after_create hook is the "Rails way", doesn't this violate their own practices?


It's okay if that's the only case where the email should be sent, and other means of user creations should never, ever do that. It might be an issue if, for example, the email should be sent always, no matter how the user was created.

Edit: basically, the question is "which process involves sending the email". It might be "creating a user," or it might be "using the 'create user' form". At some point you might need a more abstract solution, of course — see things like Listeners, DHH's Concerns, AOP or whatever you like. But the thing is, if you don't delay too long, you can retrofit your codebase with it, so don't worry too much.

Unless you're coding up a nuclear plant or storm barrier or the moon-carving laser. Please don't use webappy principles to code drivers for the moon-carving laser.


We had a case at work for not doing this. We had a section of our app where users could set up their profile, for example /user/profile (scoped to them by their session). Imagine it is more than just CRUD, and has a few bits of complicated logic (in reality there are multiple controllers - it's a big profile :D).

We then wanted to add the ability for admins to edit profiles, so we created /admin/users/[id]/profile. If the logic wasn't in the model we would have to repeat it in both controllers (the views can easily be reused by having the form elements in partials).


Of course — complicated logic is a good hint that code maybe should be moved somewhere else, be it the model or some manager class. Just like you moved (or maybe had from the start) the form elements into partials. It's probably not something to obsess about too early.

The thing that bugs me the most is how both the article and some responses here go into heavy absolutes. Different domains have different needs, and what is "readable" changes between them, not just by size, but by shape as well. And yes, shape and size might change as the app lives (my current codebase being a great example), but you can't really predict everything.


This is pretty much exactly what DHH advocates for, and for a simple case like this (creating a user, sending an email) it works great.

At some point though, your controllers are going to grow. Multiple code paths depending on the data submitted, depending on the current state of the application, etc. At this point, building service classes such as CreatesContact can become very useful, not to mention much easier to test and reuse.

Evan Light's article at http://evan.tiggerpalace.com/articles/2012/11/21/use-rails-u... advocates "Use Rails until it hurts" which I think is a very good mantra to keep in mind when working with Rails.


It really depends on what the system needs and how you are reusing functionality. Creating a user on the front end of the site with a new user sign up makes sense to send an email, but if you have a back-end admin area, creating a new user there probably wouldn't be a good idea to send out an email in many cases.

In short, use your head. There is nothing wrong with breaking out this functionality into two actions.


I like it too, and have this line of code in a few apps. It's not technically right because, in MVC, your business logic belongs in your models.

Imagine a situation where you want to bulk-create Users via script (or, simply, not via your controller "create" action). Shouldn't those bulk-created users get emails too? It depends on your business logic, hence, your model.


Well, the business logic that the given model is responsible for belongs in the model. For instance, calculating an order total belongs in the Order model if you are building a shopping cart. However, you shouldn't break separation of concerns just to keep 100% of the business logic inside your object models.

Having tightly coupled objects that depend on other classes in your application that you wouldn't expect is worse than having a little bit of business logic in your controller (or some service object), in my opinion.


These kinds of callbacks, which remind me about the "aspect oriented programming" that was hyped for a short while a few years ago, look super dangerous.

I could easily imagine someone unaware of this hook running a test to create a bunch of user entries, sending emails all over the place without even realizing.

It's like someone read http://en.wikipedia.org/wiki/COMEFROM and took it seriously.


Anybody who doesn't have something like mail_safe [1] installed is asking for trouble, no matter where he stores his business logic.

[1] https://github.com/myronmarston/mail_safe


I tried to pick a very contrived example. The architecture rails forces you to have is great if all of your business logic is simply CRUD. Once your app starts doing other things though, that's where careful consideration is needed as to where that logic should live.

You could imagine callbacks or observers that do all sorts of things:

  ping an external service
  touch another model or models
  send an internal email
  or does a combination of a bunch of stuff.
Things get even tougher when you start having flags that turn logic on or off. It has the potential to really become a mess, very fast.


That's a nice solution to the "emails gone wild" problem, but - no offense - it's a bit besides the point.

We could just as well be talking about S3 data, facebook posts, tweets, anything with (side) effects beyond the active database record in question.


If using Rails, then by default the environment specific config will (I believe) print emails to the log instead of actually sending them.


The author seems to contradict himself.

First he claims that sending an email after user creation is conventionally done in the model. Then in his hypothetical scenario a new dev comes in and looks at the controller to discern that same logic.

He completely misses the point, if its conventional to put the welcome email logic in the model then we can expect the new dev to look for the logic in the model.

The whole point of rails is convention above all.


While I don't like the name of the service object "CreatesContact", I disagree with the statement that the sending of an email should reside within the create_contact! method. The author's intent is to shield any new developers from methods that say one thing and do another, yet he adds email delivery into the create_contact! method without informing the developer what may happen (unless some magic flag is set somewhere).

A simpler, more contrived example, would be to create a new function create_and_send_email! and place the creation logic and email sending logic in there. And now, when the developer uses the service object, she has the ability to choose between just creating the contact, or creating it and sending an email.

    class CreatesContact
      def initialize(contact)
        @contact = contact
      end

      def create_contact!
        @contact.save
      end

      def create_contact_and_send_email!
        create_contact!
        deliver_contact_created_email
      end
    end


In the Obvious architecture, it would be something more like..

    class CreateContact
      def initialize contact_jack, email_jack
        @contact_jack = contact_jack
        @email_jack = email_jack
      end
    
      def do input
        # validate input
        
        contact = Contact.new
        contact.populate input

        contact_jack.save contact.to_hash

        email = ContactEmail.new
        email.populate contact.to_hash

        email_jack.send email.to_hash

        contact.to_hash
      end
    end
With that structure you can call CreateContact with:

    action = CreateContact.new
    result = action.do ContactJack.new, EmailJack.new
In that structure you can totally test the action and logic without hitting the db or the mail system at all. Your ContactJack and EmailJack can be easily swapped out for various pluggable data stores. Fileystem, MySQL, Mongo, Postgres, Cassandra, could be swapped out for the ContactJack. EmailJack could send through standard mail servers, SendGrid, Amazon AWS, Mandrill, etc.

Obvious is on github, you can read more at http://obvious.retromocha.com


I agree with your comments on the class name, it should probably be ContactCreator. But I think having a method named create_contact_and_send_email is kind of code-smelly. I'd most likely do something like:

    create_contact!(:send_email => false)


Yeah "CreatesContract" is a weird name. ContractCreator, or ContractRepository (so it can do more than just create objects) makes more sense to me.

Plus he should probably just break the send_email method into it's own method, which he can then call depending on if create_contact returns successfully.


why not use an observer to send the emails?


I like the idea of one-class-one-responsibility but have a number of questions:

1) Isn't the whole idea of ActiveRecord that persistence is hidden from the model programmer? Surely the model doesn't actually have 'triple duty' because it doesn't contain any persistence code?

"Imagine you are a brand new developer on the team that supports this app. You see the @contact.save call but now, the fact that it performs business logic is even harder to see, since the logic is placed in another module, somewhere in some other directory." - I will imagine I am a new developer on the project:

2) My expectations have been subverted. My understanding of Rails conventions is that business logic does live inside the model which will be inside app/models. Therefore, the first question I'd ask myself would be: "What business logic happens when this an instance of this model is created?" Surely Rails supports this paradigm by virtue of the existence of after_create? Wouldn't I have to visit a third class in non-obvious location to understand the updated example(app/use_cases)?

3) Doesn't CreatesContact now have two responsibilities as well? Namely Creation and validation? And the name doesn't communicate that it performs validation as well, does it?

4) Doesn't creating this new class exacerbate the problem of code/logic being 'scattered all over the place'?

5.1) Doesn't the issue of: "[In the controller], To an outside observer, it is not entirely clear that an email will be sent." remain just as problematic as the model containing the code for sending the email? Because you'd have to mentally context switch (the single biggest obstacle to understanding code IMHO) from the controller to the CreatesContact class to see what is happening. when CreatesContact.new(@contact).create_contact! is called.

5.2) Continuing the thread of the last question, CreatesContact.new(@contact).create_contact! certainly doesn't say to the programmer: "I send an email if a certain flag was checked". Isn't this unintentionally self-defeating? The aim is a reduction of 'obfuscation', but isn't hiding what a 'single-responsibility' class actually does obfuscation itself?

I don't mean to be dismissive, but, broadly, I disagree with the way you have tried to compartmentalise this application. The purpose of my questions is to help me understand your opinion.


Hey, I'm not the OP, but I have some thoughts based on my experience creating the Obvious Architecture.

1) Hiding persistance from the programmer by tying it to your models is a bad idea. It's where a lot of problems start. First, how do you test without hitting the database? How long do your tests take? Minutes? Hours? Or do you skip them?

I don't think your models should do persistance at all. They should model your data for you. Do validation against your business rules. That sort of thing.

Would you tie your models to the filesystem? If not, then why would you tie them to the database?

Pulling your persistance mechanism out of your models is the first step to writing fast, maintainable, highly testable code, even if you are still using rails.

2) Rails conventions want you to tie your DB to your objects, which is a bad idea. Rails does a lot of things well, like it gives you a nice structure for controllers, views, routing, asset management, so use it for those things. Rails is a delivery mechanism. Use it as one and leave data modeling and persistance to something else.

3) I think the name issue could be solved by treating it as "action" object. Think of actions as more like actors. They do things. They integrate models/entities and persistance mechanisms along with minor amounts of logic that don't fit inside of entities.

As an action object, you could all it CreateContact and it would be called create_contact.rb in the app/actions folder. Then, when you look in the app/actions folder, the file name communicates what it does - it creates a contact.

4) Actually, it helps a lot with testing. Creating a new class makes dependency injection super easy, making testing super easy and fast. Also, it can help with clarity and glance factor. Imagine you have an app/actions folder with files like create_contact.rb, remove_contact.rb, send_email.rb, get_contact.rb, compose_email.rb and so on. You look at that folder and you can see that it is probably an contact management and email app. Maybe an contact or newsletter management app?

That kind of glance factor you can't get with a bunch of higher level controller or service containers. It also avoids the problem of "what controller/module/service does this method belong to?"

5) I actually agree that the code that the OP did was not the best in that the persistence and email sending mechanisms aren't passed in to the action, so that you don't know what it is going to try and save and where. If the code explicitly passed in the persistence and email mechanisms it would be more apparent that an email is going to be sent as part of the CreateContact action. That being said, I would probably make those two separate actions for clarity's sake.

I've written some apps using the Obvious Architecture and it really does help solve a lot of the problems that default Rails MVC creates.


Ok, I've been working on an architecture pattern that is very similar to this idea and it's basically inspired by Uncle Bob's Clean Architecture and Screaming Architecture posts. I call it the Obvious Architecture.

What the OP writes about is a good first step and frankly, is very close to right, but why stop there? Why not ditch ActiveRecord models all together and totally decouple persistance from the "app"? Honestly, the coupling of your models and logic to a database is really where most of this trouble starts.

In the Obvious architecture the app is separate from the delivery mechanism (rails/sinatra/whatever) and also from the external persistance mechanisms like databases, queues, caching, and so on. You can mix and match both delivery mechanism and persistance methods. So, you could write a CLI app against the filesystem and then make a rails web app with a mysql persistance mechanism without changing your "app" at all.

The whole thing is built from day one to be TDD and in a recent app I wrote, it has some 78 rspec tests that run in 0.03 seconds.

I'm in the process of documenting and open sourcing the whole thing. There is a project generator gem available now called 'obvious' and you can read more at http://obvious.retromocha.com/

When I have better documentation and examples available, I plan on posting to HN, but all questions are welcome.


Rails's conventions are great, and that is what makes it rails. Move on...


If you have a use case that doesn't fit into your basic CRUD why not just create a new REST resource for it? For instance in my current app we have an InstallsController. This creates a new installation of the plugin, updates the user's account and send out an email. I've always steered clear of model callbacks as they don't play well with FactoryGirl/Machinist etc. As such the controller's create action describes everything that happens when our plugin is installed(it's still only 4 lines long).

This strategy of using new REST resources combined with modules for models where appropriate seems a very comfortable way to structure a large application. It fits into the rails conventions well and doesn't require new developers to do anything they're not familiar with. The other nice advantage is that your routes configuration gives a great overview of everything that can happen in your app.


No, really, just use modules - they are testable and reusable and any truly professional Rails dev is going to have zero problems understanding and working with them. This smells of someone very new to Ruby/Rails.


Learn your tools. New developers will need to do the same. There's zero substitute for experience. Rails solves the most common problems easily.

It's extremely common to do things upon saving. Once you need to do more than one thing upon saving you then move to a more "event" based approach.

Rails was mystifying when I first picked it up, and parts of it are mystifying today. But something as trivial as the callback chain will be second nature after some time and several apps later.


We have been moving the complex logic out of the models into libraries. But we are not happy with this approach.

We also find it difficult to cleanly develop views where data is needed from several different models. The whole Rails REST and MVC model gets in the way.

Would love to hear about how other wiser people have deal with these situations.


The last time I was on a project where we found ourselves facing that problem, we solved it by going to a Javascript-based thick client (aka, a Single Page App). The server's responsibility was to do nothing but supply a JSON API to the thick client written in Javascript.

It solved the problem nicely -- in fact, so much so that it uncovers the fact that there's a flaw in the concept of using MVC to build a server-driven web app. (Not an insurmountable flaw, obviously, because we've all done it for years. Maybe I should call it an "awkwardness" or something like an "impedance mismatch").


I think what you are in need is nothing to do with model separation. But, you need to be careful about which code belongs to your business logic. For the views you mentioned, I don't know your code but I think you may use something interim, like a presenter code which takes data from the models and helps views to show it.


> something interim, like a presenter code which takes data from the models and helps views to show it

..something like a "controller"? :P


so use presenters... easy.


I think a great way to deal with this is to have services/actions that return the appropriate hashes. Pass the hashes into your views and let them do what they're going to do with them. You won't have biz logic creeping into a view that way because views are dealing with dumb hashes, not real entity objects.


Anyone intersted in this style of programming needs to read "Clean Ruby" by Jim Gay. (Currently being written with beta access)

To be honest; it looks like this post might have been inspired by that book (or the concepts behind it) but I think the book does a better job explaining it (Obviously, a book vs a single blog post.)


I just tore through that book just a few days ago! Definitely a lot of eye-opening things happening in relation to rails and architecture.

I think a lot of people/projects/companies have been burned attempting to upgrade rails, or have a slow test suite, or having a terrible time trying to figure out where to fit core application logic into the rails stack.

And then you have an epiphany and say, wait, rails should not define my app. You say, I should be able to upgrade to rails 4 on day 1. Rails does not define my application, my application defines my application! So if you do just a wee bit of partitioning, it goes a very long way. (much to the chagrin of DHH, who for some reason evangelizes having your core logic in ActiveRecord models, to a fault)


Disappointed not to see some comments from Django/Python folks here.

Or node, mvc.net, Cake, CodeIgnitor etc etc.

Is every language/framework community doomed to repeat these debates in isolation ad infinitum?


I commented in here a couple times and I do ASP.NET MVC development in C#. It just seemed like the concepts are so similar that whether you write it in C# or Ruby doesn't particularly matter.


Seems like it.


Avdi Grimm has a "book" on separating business logic from Rails - http://objectsonrails.com/


Yes, I bought the book earlier, although it contains some valuable things but it needs an editorial review. It shows kind of things wich not simple, just complex (I said simple, not easy).


from Spaghetti code to Ravioli code




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: