MVVM, MVC it's all just roman numerals to me
Abstract
An exploration of the differences between MVC and MVVM and their relative efficacy for use on the web. In PHP specifically, a definition of MVC has evolved that does not conform to the standard definition of MVC anyway. But is MVC really the best thing to strive for?
Introduction
When I wrote the article "Model-View-Confusion part 2: Maximising View reusability with View Helpers" I had never heard of MVVM. As it turns out, this existing pattern, Model-View-ViewModel, is close to the pattern I described. I based the code on an idea presented by Padraic Brady in his insightful article "The M in MVC: Why Models are Misunderstood and Unappreciated". So firstly I must apologise for using the incorrect terminology. Although I'm not really concerned whether something is "correct" and fits a given pattern or is "wrong", I'd rather focus of the efficacy of any given approach. However, it is useful to get terminology correct where possible so I apologise for any potential confusion this may have caused.
Although this was covered in the previous article, to recap, the advantages of MVVM over MVC are that MVVM:
- Makes the view easily reusable by abstracting common logic
- Removes dependency on the model from the view
- Removes logic used only for display purposes from the model
- Allows the model to represent the state of the domain by abstracting the state of the view into ViewModels
- Gives the view a state resulting in domain models having a single instance throughout the application
- Introduces testability into otherwise untestable UI code
MVVM is a logical progression of MVC, it addresses several of the fundamental issues in the original pattern:
- There are two types of model in MVC, the current state of the system (domain objects) and the current state of the view (e.g. the id of the record current being viewed or any filters applied to a list). MVC doesn't account for them and groups them both in the Model.
- Untestable UI code
A common criticism of MVC is that there are, in fact, two types of model and MVC itself is agnostic about the difference. This criticism has been presented in several ways. For example, Deacon (2000) says:
The domain model will consist of the objects which represent and support the essence of the problem
...
The application model is the object that knows that views exist and that those views need some way of obtaining information
Terminology aside, these "application models" are what MVC itself refers to as models. The state of components within the application rather than the state of entities which represent the problem domain.
The problem with splitting models into two parts comes when there is a write operation. For instance saving data from a form. Clearly the data needs to be sent to the domain model so should the controller have access to both the application model and the domain model or should the controller pass this data to the application model which then (redundantly) passes it to the domain model?
MVVM attempts to answer this by grouping the application model with the controller to form what it calls a ViewModel.
I'd like to discuss MVVM in more detail beyond the scope of MVC and examine the merits of applying a closer MVVM approach (by not trying to be MVC). MVVM lacks any real standardisation and doesn't have the three decades worth of different implementations behind it that MVC does so it's mostly open to debate and interpretation.
MVC or MVVM
What is a ViewModel anyway?
In MVVM a ViewModel takes all the responsibilities of the MVC controller as well as storing the state of the view (for instance storing a search term which the user has entered, the "current record") and other transient states which are not part of the problem domain but only relevant to the application. Importantly, it also contains accessors which can be called by the view. This allows the view to have zero knowledge of the model. The ViewModel sits between the View and the Model and has full read/write access to the Model but has no access to the View. The View has no knowledge of the system beyond the ViewModel.
The differences between MVC and MVVM are subtle. There are still 3 components, only the responsibilities have shifted slightly. The model is now only accessed by ViewModels and the View only accesses the ViewModel. The view still fetches its own data, only from the ViewModel rather than the model. The View's only dependency is its ViewModel and it has no knowledge of the system beyond that.
Developing MVC into MVVM
In my last article I was trying to stick closely to the MVC pattern. Because of this, I never considered removing components or deviating too much from the intended responsibilities of each. One key difference between MVVM and MVC is that the ViewModel fully replaces the controller. There is often no controller in MVVM. So is a controller needed? To answer this, what the controller is actually doing must be identified and evaluated to see if it would be better elsewhere.
1) What is the controller doing?
The controller has several responsibilities (based on its responsibilities in MVC):
- Setting states on the helper ViewModel based on user input
- Updating the model based on user input (e.g. form submission)
- The controller was linked to one helper and one view.
2) Does this need to be in the controller?
Setting states on the ViewModel
As it would now be setting states on itself, all this does is minimise any potential binding logic
Updating the model based on user input (e.g. form submission)
This is a possible point of contention. In the previous example there was a one way data flow where the helper (ViewModel) had read-only access to the Model.
This allows for a better separation of concerns. The ViewModel was only ever used to read data from the model. By giving the ViewModel read/write access to the model it is now acting as a controller would in MVC, the only difference is that the ViewModel has a state which is accessed by the View.
Something intentionally avoided in the previous example was giving the Application Model access to View it was being used by. This created a one way data flow and enforced an architecture where the View had a dependency on the Application Model and the Application Model only had a dependency on the Domain Model. This kept the Application Model as thin and dumb as possible. If the ViewModel/Application Model can manipulate its view (for example, pass variables to it) then it's a step back to MVC with Fat Controllers and everything I said in Why the view gets its own data from the model re-applies. Only this time the controller has been renamed "ViewModel" and given a state. The other consideration to make is that the ViewModel is no longer thin and may require substantial logic to act as a data gateway when saving values. It may now be required to sanitise input and communicate with the surrounding environment. It is no longer self contained which blurs its responsibility and limits its portability.
Finally, in the previous article, the Application Model was basically a dumb behaviourless data transfer object. This is desirable because it allows them to be kept small and have one simple responsibility: acting as a delegate for the view, providing data/functionality that the view cannot (because it's related to the model in some way).
So there is an argument for keeping the controller; to improve separation of concerns. However, is this reason enough on its own? If the viewmodel:controller relationship is always 1:1 aren't they really two pieces of the same puzzle anyway? Is there reason to split them up?
There are justifications both ways. The way to answer this is to answer the question: Would it ever be beneficial to substitute the either just the controller or just the view model in a given triad? For testing purposes this may be desirable. It would certainly be useful to test the ViewModel in isolation free from any responsibility of handling user input. By giving the ViewModel controller type responsibilities it makes it more difficult to track down whether there is an issue in the way the user input is received or an issue in with the way the ViewModel is handling the data. Allowing switching of the controller may be beneficial on the web if the view knows of the controller. For example:
If two copies of the same triad are embedded in one page and the view (or in my example, template) creates links based on its given controller it would be possible to determine on an action which of the embedded triads was interacted with. This adds flexibility to the application.
The conclusion then, is that while a controller is in some ways redundant it's certainly a useful thing to have. It's a trade-off but one that is almost certainly worthwhile.
What I'm suggesting is a hybrid approach. MVVMC I suppose. A hybrid approach has the following advantages over pure MVVM:
- Better separation of concerns leading to easier isolation of features therefore improved testability
- Higher application flexibility
- Reusability of ViewModels with multiple controllers
and over MVC:
- Makes the view easily reusable by abstracting common logic
- Removes dependency on the model from the view
- Removes logic used only for display purposes from the model
- Allows the model to represent the state of the domain by abstracting the state of the view into ViewModels
- Gives the view a state resulting in domain models having a single instance throughout the application
- Introduces testability into otherwise untestable UI code
As I stated previously, I'm not overly concerned with whether or not a given pattern conforms to any common definition only whether it provides us as developers a measurable benefit.
Should the ViewModel restructure the model's data?
One question remains: Should the view expect a specifically structured data set from its ViewModel? To allow a completely reusable view, the format of the data should be consistent. This would allow for reusable templates. For example, a ListViewModel would always return a simple array with specifically named key/value pairs and populate a table accordingly.
As I understand it, in MVVM proper the ViewModel contains a large amount of of binding logic. For example, it would query the model for the required data and return it in a format expected by the view. One approach is to have a ViewModel method for each data item. Imagine a View which generates a table based on a data set. There are several required parts: Number of columns, column titles, styling, and records to populate the table.
My first issue with this is the amount of extra methods required by even a basic ViewModel. For example even a simple view may need a title, description, column headers, etc. Secondly it reintrodcudes binding logic in a large scale.
It would be better to return objects (such as one representing a product or a user) from the model directly to the view via the ViewModel. This goes against the principles of MVVM where the View has knowledge and a dependency only on the ViewModel. A dependency on the model is a step backwards but a worthwhile one to heavily reduce binding logic.
Finally, is this really desirable on the web? Reusing HTML templates in this way will either result in complex ViewModels with a potentially infinite number of accessors (Should this element have an onclick event? What should the css class of the <h1> tag be? Does this image have a title attribute?) or incredibly restrictive templates.
HTML allows for such flexibility that trying to re-use it in this way severely limits UI possibilities unless hundreds of views are created which goes against the idea of reusability.
The answer to this is to couple the template to the ViewModel. For example, the UserListViewModel's findAll() method would return an array of "User" objects. The ProductListViewModel's findAll() method would return an array of "Product" objects. By removing any reusability requirement from templates (Which is of very little benefit anyway) the complexity of ViewModels is reduced and any binding logic inside ViewModels is removed almost entirely.
The HTML template is really just another variable returned by the ViewModel. It contains all those variables, column headers, css class names for elements, etc. It just has them grouped in HTML. The template has a dependency on a specific ViewModel, though multiple ViewModels could potentially use the same HTML template it's unlikely in most cases.
Because of this, allowing the template to have knowledge of domain models is beneficial, even if it doesn't quite conform to the original pattern.
An implementation
The simplest way to demonstrate this is an example.
In conclusion, the parts of the system are:
Model - Stores the state of the domain. Accessed by Controllers for writing and by ViewModels for reading
Controller - Takes a user action and updates the Model and/or ViewModel where required
View - Contains the display logic related to a specific view (e.g. forms, tables, paginated data). Has a dependency on the ViewModel only
ViewModel - Stores the state of the view (including the HTML template) and has the job of fetching data from the model for use in the view
HTML Template - Really part of the ViewModel but a component in its own right. Stores the HTML which is used by the View. The template can be manipulated by the View or request data from the ViewModel
Please see a working sample code of MVVM in action here. The obvious changes from MVC are that the ViewModel has been introduced. The View could be used with any model by switching the ViewModel, and the data going into the View is fully testable. It is important to note that the ViewModel has no access to the View and the View is dependant solely on the ViewModel.
Introducing a template engine
Since I mentioned it before here is an example which shows how a template system would be introduced to separate the HTML.
What may be surprising is that the View is doing very little. The display logic is in the template. This is to avoid binding logic. The display logic for looping through a data set is better in the template to avoid binding logic. The template can read the ViewModel directly so it has no reason to be passed values from the View.
So what's the point in the View layer? The view layer is there to abstract common display logic. It would handle things like pagination, logic related to form processing as well as providing a common interface that doesn't lock your entire application into a specific template system (or lack thereof).
The view would do common logic such as applying a common filter (such as limit/offset). Beyond pagination it would deal with things such as display logic for forms (e.g. showing a success message or validation errors). It may contain logic for componentizing specific data. For example a view could draw a set of tabs. All the logic for generating the tabs would go in the view. Another example would be a table, the view would contain the logic for generating the table structure.
Conclusion
MVVM(C) enforces a very strict separation of concerns. It forces the separation of the application model and the domain model, something which is easy to miss. It offers several advantages over MVC.
Should MVC be substituted for MVVM? I believe that in a lot of cases the answer has to be yes. It's almost certainly easier to learn because there is no confusion around two types of model so it's simpler to see what goes where and offers benefits in testing and encourages a previously impossible degree of reusability. The cost of this, however is some increased initial development time as well as some added complexity. However, if MVC is being used the application is clearly non-trivial. The added complexity between MVVM and MVC is tiny compared with that between MVC and code which doesn't use a defined architecture.
Whether there is need to include a controller in MVVM is open to debate, however it does add substantial benefit at little cost.
MVVM is a logical progression of MVC and should at least be considered when deciding on a software architecture.
References
Deacon, J (2000) Model-View-Controller (MVC) Architecture (http://www.jdl.co.uk/briefings/MVC.pdf)