Live data from Hacker News

Where the logic hides in rails apps

gammons.github.com

51–56 of 56 posts

Re: Where the logic hides in rails apps

#51
post #7

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 hav…

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.

Re: Where the logic hides in rails apps

#52

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 somewh…

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

Re: Where the logic hides in rails apps

#53
post #6

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 thi…

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.

Re: Where the logic hides in rails apps

#54

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 bus…

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.

Re: Where the logic hides in rails apps

#55

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.

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.

Re: Where the logic hides in rails apps

#56
post #7

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 hav…

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