The Future of Controllers: What Does the Controller Do?
That reasonable best practice is to imagine that you had to expose your model to a Flex front end (or AJAX calls or SOAP or REST/XML requests). The rule of thumb is that if you have to replicate any code to make this work or feel that you need to call your HTML controller then you’ve probably put your business rules into your controllers and you should consider refactoring to make your model capable of being called in multiple ways by different classes of front ends.
What is interesting when you follow that advice religiously is how little there is left for the controller to do . . .
When I took the time to think about what should be handled by the controller, all that I could come up with was providing well-parameterized method calls using the URL, form and session scope, handling extremely limited navigational logic, and passing the results from the method calls to the right view(s) to build each HTML response.
Well Parameterized Method Calls
When it comes right down to it, the job of a web application is to allow you to execute commands and return the values of queries (in the widest sense of “requests for information” – not necessarily database calls). As an interesting side note, there is a great article on Wikipedia about command-query separation - the idea that every method call should be either a command (which has the potential to affect state – i.e. which may have side effects) or a query (which returns data without affecting it).
I have found that the vast majority of web requests can be modeled in terms of adding, editing, viewing, listing, authenticating, deleting, publishing, importing, exporting or reporting on a collection of one or more objects, and I am also finding that by looking at each web request as one of those categories of actions I’m able to create little domain specific languages allowing me to declaratively specify most of my model methods and to automate the generation of the vast majority of my controller code.
In general terms, if I am adding or editing data, I probably want to take information from the URL and/or form scopes, put it into the appropriate bean and the pass that to an add or edit service method (or call add or edit methods against the bean if I’m using a more “active record” style approach). If I am listing, viewing, authenticating, deleting, importing, exporting or publishing data, I typically just need to pass a small set of URL or (more occasionally) form based parameters to the appropriate model methods. It doesn’t seem like an unreasonable reach that a custom framework could be developed to do all of this through convention while still providing the flexibility to create a wide range of web applications (although possibly not the flexibility of a true general purpose framework like MG or M2).
Of course, any HTML controller must also handle session authentication in a manner that will integrate with the model.
Navigational Logic
One of the great things about M2 and MG is that they have great tools for handling navigational logic. With filters and plug-ins and listeners and events and event handlers there are a range of tools for handling really sophisticated navigational logic. But how much sophisticated navigational logic should really be in the controller? Obviously it needs to be authentication aware with either a filter or an event handler for authentication being capable of triggering the appropriate events and views. But apart from that special case, what kind of complex navigational logic is required? Most commonly it is things like the business rules that drive multi-screen wizards and complex checkout processes (if account in good standing show PO screen else show credit card screen – if credit score over 720 recommend payment plan else request payment in full, etc.). But I would argue that (a) stateless event based controllers are not the most elegant solution to this problem (read up on Seaside, a Smalltalk continuation server to get an idea of what I’m talking about) and (b) these kind of complex orchestration rules need to be part of the model. It is bad enough writing them once without having to create a different implementation for every mode you support. Typically these will either be folded in to the existing service layer or will be added as a separate orchestration layer/facade to the service layer.
I’m still really thinking this through, but does anyone have good examples of complex navigational logic that SHOULD definitely stay in the controller that isn’t tied to authentication (which is a little different between say HTML requests and web service calls)?
Calling Views
Then of course the final thing that the controller has to do is to make sure that all of the necessary data is passed to all of the necessary views to build up the page that in turn is returned to the clients browser on the completion of the request.
So, it seems to me at first blush that one of the main things that implicit invocation frameworks are particularly good for (more complex navigational logic) is something that we should probably be removing from the controller to be replaced by a more sophisticated session aware state machine as part of the model so it is easier to program complex logic and so that the logic is somewhere that it can be accessed by all authenticated calls – whether from HTML, AJAX, Flex or web service calls. But if we remove that from the controller, is the current approach to controllers the optimum one? (And yes, this is a genuine, not a rhetorical question.)



This adaptation between the UI and the backing application is the primary concern of a UI controller. And in my mind, it's quite a bit more important than the vastly more glorified task of taking some data from the model and handing it off to a view for rendering. The latter makes a website from a database, the former makes a web-based interface for an existing application (which may or may not happen to be developed at the same time).
Note that what I'm referring to is very different than the "navigational logic" you describe. Clearly, if the logic depends on business knowledge, it belongs in the model. But where it's UI-centric, that's the controller's job. As is well known, I'm a Fusebox guy, but as you point out, the implicit invocation nature of Mach-II and Model-Glue can greatly ease the task of implementing "stateful context changes". However, the simple fact is that both frameworks (along with Fusebox) are UI frameworks at heart, not application frameworks. You're not going to be dispatching M-II/M-G events from your application components, you're going to be doing it from your listeners/controllers, based on return values and/or exceptions raised by your application components. So it's still a paradigm of "listen to the model, react in the UI", regardless of what framework you use up top. And if you slap a different frontend on there, you're going to have to reimplement the logic to suit the new UI, though based on the same messages from the application itself.
So compared to the other pieces that comprise a successful application, the controller definitely has a very thin slice of the pie. But while it's a thin slice, it's the only piece of code that end users actually relate to, so it's arguably the most important. You can cope with a "thrown together" backend for a while and fix it down the road, but if you have a poor UI, your ship is sunk and no amount of stellar backend is going to save you.
Thanks very much for the very detailed comment. I wanted to take some time to think about your posting which is why I didn't respond immediately.
I think you've made a REALLY interesting distinction between UI driven flows and domain driven flows. While the two different drivers are a conceptually important distinction, from a design perspective I'd probably ask whether the flow was likely to change between UI interfaces. If you have a form that is always completed in three separate steps (whether the reason is busness validation or workflow or just that ALL of your conceivable UIs would want to chunk the data the same way) that flow belongs in the model. If the chunking is UI specific, then that chunking support would reasonably belong in the model. Very interesting concept. Must think (and blog) about this some more.
As always, very insightful - seems like I always learn something when you put fingers to keyboard - thanks!
I'd build the backend with a single method that takes all the parameters from the form/forms (probably as a bean), and does whatever it does. Then your Flex UI can just package it up and send it off, while your HTML form will incrementally build a session-scoped bean, and then when it's all done, send it off.
Where you want the model to be aware of the distinction is when you've got business logic built into the flow. For example, a three part form for a ecomm checkout, but where page two is optional if it's a returning customer (i.e. only new customers need to fill it out). Clearly, in this case you can't leave it up to the UI, because the distinction is part of the business logic.
I think there's also a spot for the "halfway" point. There may be things that have nothing to do with the business logic of your application, but which are common to all your different presentation layers. You don't want to replicate them in each UI, but at the same time you don't really want them cluttering your model either. So you create some sort of "controller utilities" location, if you will. I personally see this more between distinct-but-related apps, all running an HTML UI. With multiple Fuseboxes, you'd probably do that with shared circuits. Between Flex/CF, you'd use a utility CFC that's remote.
I agree 100% with the 3 page form. If that's an accordion in flex, there should be only 1 sumbit in the model (at least in an ideal world), and the idea of the form building up a session scoped bean makes perfect sense (we could talk optimization for heavy traffic where form based would be more fragile but more performant, but that's a different question!).
I agree that with business logic you should be returning to the model (as per the optional page 2 in the checkout).
I also agree that if all presentation layers will always use three forms, it doesn't matter whether there is business logic, you can encapsulate what (doesn't) vary and put that in the model.
To be honest that was what I was trying to say in my comment, but I think posting three specific examples made it much clearer - thanks!
Now I just need to find a way to declaratively specify all of these options :->