Thursday, January 08, 2009

MVC explained

... to myself, as I'm trying to learn it :)

This is how I'm organizing a project I'm currently working on:

  • View - this is used to render the model as HTML, since this is an ASP.NET project. The View can contain view logic, like displaying negative numbers in red.

  • ViewModel - this is not necessarily the domain model (database table), but a view-specific model. An Order view, for example, needs to display data from an Order, multiple OrderDetails, a Customer and possibly a Seller (and maybe more). Right now, I handle this case by making a composite class with several properties:

  1. public class OrderModel  
  2. {  
  3.   public Order Order { getset; }  
  4.   public IEnumerable<OrderDetail> Details { getset; }  
  5.   public Customer Customer { getset; }  
  6.   public Address ShippingAddress { getset; }  
  7.   public bool CanEditShipping { getset; }  
  8. }  

This is basically a DataTransferObject and is used as such by the view. I realize it would be safer if I created an immutable struct with the necessary data and pass that to the view, but I am too lazy and don't have a tool to do that for me right now. I think this is what Microsoft tried to do with their ViewData dictionary, since it's (normally) strictly write-only for the controller and read-only for the view. This might be easier in C# 4.0, because I can discover properties at runtime, so I can write something like this:

  1. return View(new { Order = order, OrderDetails = order.Details, Customer = customer, ShippingAddress = order.ShippingAddress, CanEditShipping = true });  

and still use Model.CanEditShipping in the view. (It still requires some discipline to use Model.OrderDetails instead of Model.Order.Details, of course.)

  • Controller - this has three responsibilities in my application: to use the business logic to retrieve the domain models (representing records), combine those into the model as defined above and choose the right view to apply or the correct path to re-route to (this is the "controller logic"). The controller is very "skinny", usually a couple of lines per action.

  • Repository - this is the business logic.

  • Domain model - aka tables / records / plain C# objects. Can contain "model logic", like a GetMainImage() method on the Product object that can decide to "promote" an image as main if none of those associated with the product have been flagged as such.

Any glaring mistakes?

Edit: I just found this article by Jimmy Bogard

Edit (Jan 16, 2009): Oopsie. It seems that I'm going to have to revise this.

No comments: