MVC Revisited

I’ve just become aware that there are differences of opinion regarding the actual roles of each aspect of the Model-View-Controller (MVC) framework. So I thought I’d weigh in on the matter.

It should be born in mind that this is my opinion, not necessarily “best practice” or anything else. You can see if you agree or disagree once I explain it.

First, the View is more or less stupid. The data the View needs is tailored to the View, and the View simply iterates through that data and displays according to whatever recipe is in the View. The whole point of the View is simply to display the data fed to it in whatever way the programmer has designated. The data itself comes from the Model. The View doesn’t call for the data. There isn’t any active mechanism in the View which calls for this tidbit, then that tidbit. The View is not an active participant. The data for the View is provide by the Model as dictated by the Controller.

The Model provides the data for the View. It provides the information needed for the View to function, no more and no less. The Model should be tailored with recipes of data collection which exactly fit what the View needs. Neither the View nor the Controller need know how the Model does its business. In fact, the less the Controller and View know, the better. What kind of backing store there is, the tables, their structure and population should be exclusively the concern of the Model. The Model will likely be filled with SQL calls or file opens and reads, for the most part. When it comes to updating the information in the backing store, the Model should again be in charge. In fact, most of the code for the MVC should be in the Model. Most of the intelligence should be vested in the Model.

The Controller should dictate to the Model what information the View will need and then provide it to the View. The Controller should be the last stop for inbound data to the backing store. The Controller should fetch information from the user and provide it to the Model for disposition by the Model. Whatever data the Controller passes to the Model or the View shouldn’t be questioned by the Controller.

In terms of design, the way to make all this work is to figure out, first and foremost, what the user needs and wants from the View, and then design the View so that it displays the data as needed and designed. Only in this way can the system know what the outbound data must be. Once the View is more or less built, the data is known, and the Model can undertake the task of providing the View with the data it needs. Once this data is known (and its shape), the Model can be tailored (with additional routines as needed) to provide exactly what the View needs. There may well be a different routine in any Model for each specific View. Once the Model’s views have been built, the Controller is then written to work out how the View will be provided with its data, and how the inbound data can be returned to the Model for storage. So we essentially work backward in design, starting from the View, working our way through the Model, all the way back to the Controller.

One still open question about the design of this system is which components protect others from data corruption and how. The user is asked for a full name, but omits the last name. This omission should not be of concern to the Controller, even though the controller passes the data onto the Model for disposal. The Controller is only concerned that the state of the system has changed, and should react to the change of state by taking the appropriate action. The data gets turned over to the Model to do something with. Should there be omissions, data of the incorrect type, etc., the Model should then inform the Controller, which instructs the View to tell the user that they have made a mistake. The Controller then awaits a correction from View and then once again forwards the data on to the Model for disposal. This means that the Model must act on its own to defend the backing store from erroneous input. The Model may do this in various ways, but most straightforward is to simply check the inbound data before attempting to store it in the database or files or whatever. It must have some mechanism for informing the Controller (usually a error return code) and the user (via the View) that a mistake has been made. An error changes the state of the Controller and forces it to deal with the problem by directing the View to take action as needed.

The defense of the backing store (particularly in the case of a SQL database) can be left to the store, but this is often an operation which involves exceptions and is messy. It is better to force the Model to deal with data anomalies. Besides, certain error conditions will simply be accepted by a SQL back end without question. That being the case, it’s best to force the Model to handle this task on its own. Omissions, incorrect data types, etc., can all be detected and handled by the Model.

There such a thing as an unlikely or impossible error condition. For example, within the Model, one method may fetch the data for another method. In this case, as long as access to the second method is restricted to the caller, there is no need to check the data provided. For example, one method is designed to fetch the first and last name of a customer, and another is designed to fetch their credit card number, based on first and last name. The second method, being restricted to being called from within the Model, shouldn’t need to worry about checking to see whether the first and last name are both present. So there’s no need to load it up with error-detection code. Only where absolutely needed should error code be added.