In my introduction post, I have mentioned that I will collect the problems or questions that I have encountered during my MVC research. So this is the first one.
As I have discovered these problems during the rewrite of our code sample for Genome, which is a simple web shop, I’ll use the terms and use cases of that sample. The sample can be downloaded together with the evaluation version of Genome, but even without downloading it you will probably understand the concept. It is a simple web shop with products, categories, shopping cart, order and a simple user class.
What is the model?
If you create a new MVC web project with ASP.NET MVC wizard, it creates three different folders for models, views and controllers. However, if you watch the “getting started” videos, the “Model” folder is always left empty or even removed from the project (StoreFront). What does this mean? Where and what is the model really?
Based on the original MVC pattern, the model should represent the data and the business logic that manipulates the data. It is also an important point that the model does not know about the view, in order to ensure the desired separation.
So if we transform this to today’s .NET multi-tier web development, than we get the result that the model is the business logic and the domain model of our application. Which is normally separated into a different layer anyway – a different assembly (sometimes even behind a (WCF) service interface). So probably that is the reason why the model folder is always empty – the model is in a different assembly.
The business logic, especially if it follows some kind of SOA pattern, usually separated by the different function groups of the application. So there are different business logic methods for product management, ordering, user management, etc.
The user interface is, however, usually not separated so well. The user login and the shopping cart status are displayed in every page. You can put the items to the shopping cart directly from the product display page and you might want to display the categories even in the shopping cart page. The different functionalities of the application are composed together in the displayed pages – the views.
As the model does not know about the view, the controller should collect the data from the different business logic functions and pass it to the view. As in the MVC framework, the view can only have a single model (not considering the ViewData state bag, as it is not type safe and should not be used heavily IMHO), this means that each view will have a class that groups together the different domain model pieces (let’s call it view data class). But where should this class come? Does it rather belong to the view or rather to the controller? In the “getting started” samples, this class is usually together with the controller (as a nested class of the controller class). I don’t like public nested classes, so I placed these view data classes next to the controllers in separate files. But I’m not fully satisfied with the result. I think to mix these classes to the views (especially since they are aspx/ascx files, and not even pure C#) is even worse. But we can also see these classes like the projections of the domain model to a concrete use-case. From this point of view, maybe they belong to the model a little bit. Should they maybe come into the “Model” folder in the web project?
The SelectList problem
It is probably an interesting fact that (at least in the public samples) sometimes also very user control specific data classes are used as view data, for instance SelectList. Based on what the MVC pattern says, this should be OK, as this information is passed from the controller to the view, and the controlled may know about the view details. Maybe the view data should be much more view specific and more detached from the domain model? (You might think that this is just a minor question, but if you want to setup an architecture for a project where multiple developers work on different parts of the application, you have to agree, what kind of data you pass to the view, and where you do the different transformations.) Is it the responsibility of the view to transform the model data to user control specific representation or is it the responsibility of the view? Based on my MVC pattern knowledge, this should be the view, but I’m not sure. But SelectList under the ViewData.Model property is suspicious for me somehow…
Refactoring view data
I have another question regarding the model data,too. Let’s suppose that I have a view that has to display a simple domain model entity like a shopping cart. Currently I don’t have anything else in the view, so the ShoppingCart class can be used directly as a domain class for this view. But should I use it directly?
If the view will be extended later, probably other things must be also displayed in the view (e.g. the category list), so I will need to introduce a view data class that groups the shopping cart and the category list together. Introducing such class requires a painful refactoring in the view. With the current refactoring tools I know, you cannot automate this step, you have to go through all the cases where you have used ViewData.Model and should replace with ViewData.Model.ShoppingCart. (Well, maybe you can use search and replace, but still not nice.)
I see two possibilities to avoid this situation:
- You always introduce a view data class when creating a new view, even if it’s embedding a single class only.
- You don’t use the ViewData.Model directly, but you always create properties in the code behind that access the elements in the model class (in this case a ShoppingCart property that returns the ViewData.Model). If you have to extend the view data, you can simply change the property implementation and you don’t have to change the aspx code itself.
Unfortunately I don’t like either of these. Solution a) causes probably an unnecessary overhead for most of the views (the ones that will never be extended), and also causes too long data access expressions (ViewData.Model.ShoppingCart.Items.Count). Solution b) decreases the gain that we have achieved by having the generic view base class and the strongly typed model. It also increases the usage of the code behind file, which is the first step to have logic sneaked into the views that we wanted to avoid with MVC. Also it is hard to enforce that everyone keeps this rule and does not access the ViewData.Model directly, causing a big mess in the view. But I think altogether I like solution b) more, probably because the long data access expressions anyway mess up the view aspx.