Here’s an idea that may work for some of us. I’ll try this next week if, and only if, I run into annoyances:
Let’s take an example:
- We have a Model class. The model class is just a placeholder for data.
- We have a Request class. The request class is just querying a server for data.
- We have a controller that (a) fires the Request and receives its response and (2) passes the result to the model.
Does the controller need to effect this operation? Think not. Maybe the controller shouldn’t control this.
The business logic argument
The described operation is exposing too much of the model in the wrong way. The controller should care about how the model updates only insofar as the view may need to render something about it.
If we need the view to let the user know that a request has been sent, what we’re really asking for is the model to fire an event to let us know, and the controller to handle this event.
The clarity argument.
Controllers easily get intricate. If we can move something from the controller to the view or the model, we should.
I like to have struct like classes that not define any operations. This isn’t incompatible. More often than not, model operations can be neatly defined using commands. Otherwise, we can fall back on a model/data split, where the model is a kind of ‘data controller’ viz. the model holds no state, only business flow, and the data holds no function, just data.
The integration argument
If the controller is manipulating view and model states too finely, service integration is reduced. Instead of having qualified, usable views and models, we have particulate, D.I.Y views and models that turn out, more often than not, to provide the kind of flexibility attracting bugs and reducing maintainability.
A particular, enlightening scenario is when the model or view expose sufficient state that they could not avoid being put into an inconsistent state by a careless controller. Hey, we got a nice little rule here:
If a view or model’s interface allows putting such view or model in an inconsistent state, there is too much logic in the controller.
An organic, yet ideal controller
An ideal controller should…
- Hold no state
- Receive events from the view and model
- Issue high level commands mapping the model to the view, and vice versa.
- Be thin.
- Look boring and redundant.
Why (5)? well, that’s good, and that’s why we often wonder what the controller is for. Here’s an example:
- Play button is pressed and fires an event
- Controller handles the event and invokes the model’s play method.
That’s boring, yet performs an awfully simple function: decouple the view from the model. Because it is boring and simple, it also makes a fair allowance for ‘becoming more complicated later on’ without anticipating over much anything at all. This isn’t planning what features we’ll need in six months, it’s just a kind of placeholder for future intricacies.
On the other hand, here’s a counter-example
- Play button is pressed and fires an event
- Controller handles the event
- Controller creates a download stream.
- Controller receives a callback to indicate the stream is connected (and sets a ‘control state’ flag)
- Controller changes the color of the play button.
The good news
Interaction can easily get complex. Models and views can also get complex. Let the controller be thin and stateless. If our controllers are more concerned with the semantics of interaction, and less with rendering and model updates, they are better controllers.
The bad news is, it’s easy to push too much logic in a controller. The good news is, it’s easy to take it out.


Comments