Where should I define a `save` action?

@alexspeller is right on. And to the degree that this matters, I’ve read the same thing from @tomdale, I believe. The router and your routes are the state machine for your app. Controllers can be used anywhere outside of their default context and you don’t want your data control wandering aimlessly around your app. Users also have a nasty habit of navigating away from a page before everything is cleaned up or handled as you intended.

I do see how moving CRUD operations from a controller to a route can improve maintenance when changing the template. However, by doing so doesn’t this violate the “Tell don’t ask” principle? Controller has all the information to make a decision and act on it but you’re asking about this information from a route which make the route know about controller’s internals.

2 Likes

Yeah, I think this is the thing that’s been troubling me slightly, I’ve always tried to use a lot of small controllers and keep the actions as localised as possible. I’m curious to try this new structure though so I’ll give it a go and see how everything shakes out.

Thinking about this a bit more… if all actions are moved to the routes, are we not just using controllers as presenters and routes as controllers?

1 Like

What about doing your data logic on the Controller action and then return true; to bubble the action up to the Route for transition needs?

Yes, this is the point and is intended. They are misnamed IMO. Or more accurately, they are not supposed to be viewed the same way as controllers in rails would be. Routes in ember are much more like controllers in rails.

Possibly and possibly not. I’d argue that the action is telling the route what to do, and the controller is just storing the necessary state to make the decision. If you view the controller solely as a state store / presenter then this is not an issue, and I argue that’s the best way to look at it.

Even if it does violate this principle, I’ve worked on large apps that take both approaches and have definitely found the route centric apps far simpler to work with and maintain.

I don’t think it can be split as simply as that. The idea is not to “only handle transition logic on the route”. The idea is to handle the majority of the logic in the route, and this approach still has the drawbacks I described above. If you want your app to function differently in different states, (i.e. alert(‘can’t edit whilst previewing a post’) or similar logic) then you have to have the data logic in the route. In fact, I’d say I like this approach even less as it splits up the action into pieces and makes it more difficult to follow.

1 Like

I was linked to this thread by alexspeller when we were discussing this same thing on the #emberjs channel.

(I would have links to individual parts of the code, but I’m only allowed to include 2 links because I’m a new user. So here’s the code.)

I’m re-building Spree’s order backend interface using Ember and I’ve got an OrderStateRoute which is responsible for displaying the individual states of an order. They are by default: cart, address, delivery, payment and complete.

On the cart state, I’m rendering a list of the line items in the order which lets me have a Backend.OrdersLineItemsShowController where I can define actions for the line items. Similarly in the delivery state I am rendering a list of shipments (which is a partial because it needs to be re-used on the complete state page), and that eventually gives me a Backend.OrdersShipmentsShowController where I can put all the actions for the individual shipments. I also render a list of payments in the payment step and surely by now you see where this is going!

alexspeller and others have been trying to convince me that this is the wrong thing to do, and so far I am utterly unconvinced.

I think it’s more logical to have the actions within the controllers that have been generated. These templates are not used anywhere else outside of the context of an order, and so there is no need for concern about re-usability. If I put these actions within the OrdersStateRoute, then that one file will have logic for line items, shipments AND payments and, in my opinion, that is extremely messy and illogical. If I want to find the actions for a line item, then I should be looking in the file that has “line_item” in its name, and not the OrdersState route. It just doesn’t make sense at all to be looking in the routes.

So please educate me in all the ways that I am wrong and I would gladly fix this heinous code.

Thanks.

3 Likes

If you’re referring to this: https://github.com/radar/spree/blob/ember/backend/app/assets/javascripts/spree/backend/orders/controllers/orders/line_items/show.js.coffee

Then stop worrying, you’re doing it right™

1 Like

There doesn’t seem to be a settled view on this, I’m certainly getting a cloudy picture about what the ember golden path is anyway. Going back to principles, “keep computation close to it’s data” (can’t remember the original), doesn’t it seem more appropriate to put actions in the controllers with the data that they act upon?

I agree with @radar. I would lean more towards controllers for interactions with the page. The exception for me is anything related to routing, which I would handle in a route. If I was concerned about duplicated logic, I’d probably lean more on Mixins for controllers than pushing the logic into the routes.

I’ve started playing with using command objects to perform complex business logic to slim down the router.

Eg a simplified example where after saving a task we enqueue a background job to send an email:

TasksRoute = Ember.Route.extend
  actions:
    save: (task) ->
      @commandFor("create-task").exec(task).then =>
        @transitionTo("task", task)
CreateTask = Ember.Object.extend
  project: alias("controller.project")
  person: alias("current.person")

  exec: (task) ->
    task.save().then =>
      @queue.publish("send-email", "new-task", {
        by: @get("person")
        project: @get("project")
      })

The routing/transitioning logic stays in the route, with commandFor the command automatically gets wired up with the current controller and some other injected stuff but doesn’t have access to the route.

In the actual app some of these command objects get quite large but are still nicely self contained & easy to test whilst keeping the routes easy to see what’s going on.

Similar to the skinny model, skinny controller pattern in rails.

5 Likes

Routes can and often handle multiple controllers, so it makes sense to be handling different models (line items and orders).

I think it’s mostly about error handling. Im not sure controllers know anything about promises, for example. They should though, it’d be great.

A lot has changed with Ember since the last post in May - has the thinking within the community on controllers vs. routes for actions changed at all or is it still a Do What Feels Right™ sort of thing? I have no real prior experience with any MVC frameworks (C#/Webforms guy) so I don’t have any inherent bias towards any particular pattern.

I’ve put together a gist illustrating the pattern I’m currently using which is similar to what @alexspeller recommended earlier in this post.

I’d generally recommend putting actions on routes in preference to controllers, especially if you’re going to do things like transition, which is a routing concern and not a controller concern. Controllers are there to store your in-memory application state, wrap your models to present them to your templates, and connect your models together using dependency injection. Routes should be where the bulk of your logic and actions live. In my experience you will find your application easier to maintain if you keep your controllers as skinny as possible.

There are some other great reasons to prefer routes to controllers for actions, for further details see this talk (vid at bottom of page): http://madhatted.com/2013/8/31/emberfest-presentation-complex-architectures-in-ember98

Basically I perform any data validations in the controller and send the action on to the route for the actual CRUD operation if the validations pass. Am I crazy for combining my actions in both controllers and routes?

2 Likes

@ultimatemonty

That is exactly the same approach I take. I basically replicate action handlers in controllers and routes. Validation is almost always a controller or model responsibility and if we consider that all network/API communication should be encapsulated into the Route, then after successful validation in the controller, the route should then handle the event to perform the network hit. I would think that over time, if this paradigm becomes the standard method, enhancements to controllers, routes, and actions would be built into Ember to make this workflow “feel” more correct and work more by convention as do most core Ember features.

That being said, I’d love to hear more about other approaches and why they might work better.

Lately, I’ve just been using a SessionController with no corresponding SessionRoute.

Using routes in this way seems complete nuts to me! Not bad - just so different to every other MVC type framework I’ve worked on.

Part of the confusion for me as I’m learning ember is that because there are options on things like this it’s sometimes hard to think about the “correct” way to do things. Also because ember has had so much work done on it (including cli) - lots of the online stuff is outdated.

I’m getting my head around it now but it’s not been easy!

Ember is more of an MVVM framework, but now with Components, I don’t even know what the acronym is anymore. Here’s a good talk by Matt Jones from Neo Innovation that will definitely help if you’re coming from a more traditional server-side MVC background: http://www.confreaks.com/videos/3460-emberconf2014-empty-your-cup-reflections-on-learning-ember

When comparing to Rails the naming sounds completely different but the concepts seem to map fairly well (at least in my understanding):

Router.map == routes.rb
Route      == Controller
Controller == Decorator
View       == View (Template + jQuery)

I mentally map it to a “page” in Rails having 1 Controller (route) that pulls all of the necessary data together to pass off to various decorators (controllers), each responsible for building one part of the final page.

When it comes to nested routes in Ember, that is the same as having nested layouts in Rails, albeit with each layout having it’s own controller/decorator with the necessary data passed in for you.

It’s probably not entirely accurate but it certainly helped me grok Ember architecture when I first started, maybe it can help someone else too!

1 Like

If routes are like Rails controller i would prefer to store my CRUD actions in routes. But What about nested CRUD? For example say that we have a post model and a comments model. I would like to have my action for creating a comment for a post in my comments route is this correct?