The frameworks are important. They offer libraries that make the implementation of the pattern cleaner by encapsulating functionality that is not offered in the underlying XAML engine. What is more important is that we have a good understanding of the pattern itself before diving in to one of these frameworks. If we understand the "why" of the pattern, then we are less likely to make pattern-breaking errors when we get to the "how" of the implementation.
In a previous article (XAML App Review - Part 5: MVVM and Wrap Up), we took a very quick look at MVVM. Today, we'll dive deeper into the pattern itself: a little bit of history, the problem spaces that it is designed to resolve, the various parts that make up the pattern, and some consequences of using the pattern. In a future article, we'll look at a sample implementation of the pattern in a XAML-based application.
A Little Bit of History
The MVVM pattern is based on the concepts of previous Presentation design patterns including MVC (Model-View-Controller) and MVP (Model-View-Presenter). The MVC pattern was developed before the RAD tools that we use today, when the developer had to build the UI elements from scratch (or bring them in from a shared library). Andrew Hunt and David Thomas describe the key concept of MVC as "separating the model from both the GUI that represents it and the controls that manage the view" [Andrew Hunt and David Thomas, The Pragmatic Programmer, Addison-Wesley, 1999].
These patterns have moved forward into our modern tools (as with ASP.NET MVC). As we can tell from the names, these patterns share common ideas: the Model and the View. We will discuss each of these in more detail below.
The MVVM pattern is a variation on the MVC and MVP patterns based on the presentation model of XAML. XAML offers a very rich binding mechanism, and the MVVM pattern uses this binding mechanism as the "goo" that holds everything together.
A Bit of Review
Christopher Alexander gives us a definition of design patterns that we use today:
Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over without ever doing it the same way twice. [Alexander, et al., A Pattern Language, Oxford, 1977]One of the important parts of this definition is that a pattern describes a core of the solution. The pattern does not describe a particular implementation. There are tons of frameworks that help us with implementation: Prism, Caliburn, MVVM Lite, and many, many others. These all assist us with implementing the MVVM pattern (and as noted, we may be most successful with one of these frameworks), but no implementation is the pattern itself.
For more information on Design Patterns in general, please refer to Learn the Lingo: Design Patterns.
For simplicity, we'll stick to the four basic parts of a design pattern: Pattern Name, Problem Space, Solution, and Consequences. We've already seen the Pattern Name: Model-View-ViewModel. We'll see how this describes the different components as we get to the Solution. Normally, we would describe the Problem Space before the Solution, but because of the nature of the pattern, it will be easiest for us to look at the Solution first.
As the pattern name implies, the MVVM pattern consists of three primary components. Note: I'm using the word "component" here in the generic sense ("parts" or "pieces"). After taking a look at each of the components, we will see how they interact with each other.
The Model encapsulates the business logic and data that is used by the application. The best way to think of the Model is everything that is not Presentation -- when using a layered application approach, it is everything below the presentation layer (which may include a business layer, service layer, data access layer, data storage layer, etc.). In most scenarios, our Model will be abstracted into various components. For example, we may have a repository that is responsible for retrieving and persisting objects through a service, and the service is responsible for interacting with the data store (such as a database). And what types of data objects are exposed will vary; they may be smart objects (if we are doing Object Oriented Programming) or data entities (if we are doing Entity or Domain-Based Development).
The actual implementation of the Model is not important to the overall MVVM pattern, although the implementation of the objects in the Model may affect how the objects are exposed in the ViewModel. The Model should not have any presentation logic.
The ViewModel encapsulates the presentation logic and the data that is used by the View. We can think of this as the "goo" that holds things together. MVVM is designed around the powerful data binding engine that is available in XAML. The ViewModel is responsible for taking the data objects that are available in the Model and making them available as properties to which the View can data bind.
But the ViewModel is responsible for more than just exposing Model objects as properties. It also provides properties that are used for the presentation logic of the application. For example, lets say that we have an Order object, and whether it is read-only will depend on whether the order has already shipped. The Model would handle determining the read-only state of the object (since this is part of the business logic), but then the ViewModel could expose an OrderReadOnly property that the View can use. The View can then bind the IsReadOnly property for a particular control (or set of controls) to the OrderReadOnly property of the ViewModel.
The ViewModel also handles any interaction between the View and the Model. This is accomplished by exposing methods or commands that the View can call. The ViewModel then uses those methods to make calls into the Model. We'll take a look at this more closely in the Interactions section below.
The View is what the user sees on the screen -- all of the UI elements. This is the collection of XAML elements that make up the user interface (user controls, buttons, labels, text boxes) as well as the supporting items that assist these elements (styles, animations, control templates).
As we've seen, MVVM is based on XAML data binding. This means that our view is usually chock-full of data bound attributes. As noted in the ViewModel description above, this is not limited to data, but also other properties that affect interaction. For example, we may have a TextBox that has its Text property is data bound to a property in the ViewModel (such as OrderDate). But that same TextBox may also have its IsReadOnly or IsEnabled property data bound to properties in the ViewModel. We can even have visual properties like FontSize or Foreground determined by data binding to ViewModel properties (along with appropriate value converters).
Separation of Concerns and Interaction
Now that we've seen the components and what they are responsible for, let's take a look at how they interact with each other.
The View only knows about ViewModel. The View does not have any direct knowledge of the Model. It only knows about the ViewModel. The View is generally responsible for creating the ViewModel, and this is almost always a one-to-one relationship. In an ideal world, the View would not have any code-behind (but some people are more adamant about this than others -- it depends on the implementation).
The ViewModel knows about the Model. The ViewModel is responsible for retrieving any objects that it needs to expose from the Model. This can be a one-to-one or one-to-many relationship. An example of a one-to-one relationship may be a ViewModel that exposes a Customer object from the model; an example of a one-to-many relationship may be a ViewModel that needs to expose an Order that contains a Customer along with multiple Products. How these are exposed (as single or composite objects) is dependent on the needs of the application. The ViewModel should depend on the Model for business logic (for example, calling to the Model to determine whether an object is editable based on the object's data values).
The ViewModel does not have direct knowledge of the View -- meaning, the ViewModel should not reference any of the controls or UI elements that are part of the View. It can contain Presentation Logic (for example, exposing and setting properties that determine whether controls are shown or hidden), but it should leave it up to the View to determine how the elements are actually presented.
The Model does not have direct knowledge of the ViewModel or the View. It is up to the ViewModel to handle any interaction with the Model (never the other way around). The Model should contain the Business Logic, but the Presentation Logic should be limited to the ViewModel.
Example: Property Interaction
Let's take a look at a specific business rule that affects our user interface and see which components have the responsibility along the way. To go back to our Order example, let's say that the Order cannot be changed after it has shipped. The Model would contain the business logic to determine whether the Order has been shipped (perhaps by checking a ShippingDate field) and then set a ReadOnly property on the Order object appropriately. The ViewModel would take this ReadOnly property and expose it to the View. The View could then determine how to use this property through data binding. For example, the View may have the IsReadOnly property on a set of TextBoxes data bound to the ViewModel property, or it may completely change the visual elements by showing TextBlocks if the ReadOnly property on the ViewModel is set to true. The important part to remember is that the Model is responsible for the business logic (determining whether the Order is changeable), the ViewModel is responsible for exposing this property to the View, and the View is responsible for using the property to control the actual UI elements.
Example: Method Interaction
Now, a different perspective: let's say we have a Save button in our View, and we need to save the current object on the screen. Note: there are several ways of implementing this; this just shows one way. In our View, we have a Button with its Command property data bound to a SaveOrderCommand in the ViewModel. In the ViewModel, the SaveOrderCommand object executes, which calls the Repository<Order>.Save() method (in the Model) and passes in the current Order object. In the Model, the Save method in the repository performs the appropriate action, such as saving the Order to the database. Note the interaction: the View only calls into the ViewModel, and it is up to the ViewModel to call into the Model. Then the Model does the actual work.
MVVM: Problem Space
Now that we've seen the various components of the MVVM pattern, let's take a look at why we would want to use the pattern. At its core, the pattern addresses three main problem spaces:
Problem Space: Mixture of Presentation and Business Logic
Application developers often mix Presentation Logic code and Business Logic code together. It's just too easy to do this with our modern tools: just open a new WPF project in Visual Studio, drag-and-drop some controls on to the XAML workspace, flip over to the code-behind and start slapping code together. But coding this way leads to a maintenance nightmare.
Solution: MVVM encourages good separation of Presentation and Business Logic. With clearly defined components, the developer has appropriate places to put the presentation logic (the ViewModel) separate from the business logic (the Model).
A small caveat: notice that I used the word "encourages". There is nothing in the pattern (or any pattern) that forces you to put code in one place or another. Once we start to put code in the wrong area, we end up breaking the pattern, but our code might still work. We need to be attentive that we are actually putting the code in the appropriate area. If we start to break the pattern, then we will lose the benefits.
Problem Space: Unit Testing of the Presentation Layer is Difficult
When we are unit testing (which should be always), our goal is to have tests that cover as much of our code as possible -- this includes the Presentation functionality. When developing unit tests against a traditional UI (event handlers in the code-behind), it is difficult to split out individual functionality. For example, if we want to unit test a Button's click event, we can create a unit test around the event handler itself. But in the set up for the test, we will need to instantiate a Button object (the "sender" parameter) by using a fake or a mock. This may also mean that we need to instantiate an entire UserControl object as a parent for the Button in order to get a valid test.
Solution: The ViewModel in MVVM lends itself to cleaner unit testing. Using the example above, let's say that instead of the Button click event performing an action directly, it calls a method in the ViewModel. Now, we can easily unit test the method in the ViewModel. Since the ViewModel does not have reference to UI elements, we would not need to instantiate them for the unit test. This leads to code that is easier to test and is easier to isolate. Note: method calls will generally travel from the ViewModel to the Model, but because we have these "seams" in our application, it is easier to fake or mock those interactions in our unit tests.
Problem Space: File Sharing Between Designers and Developers
In situations where there are dedicated interaction designers and application developers, it can be difficult to coordinate work since the designers and developers need to interact with the same set of source files (for example, the MainPage.xaml and MainPage.xaml.cs files that travel together in source control). This can get especially tricky when both areas need to check in changes to the same files at the same time. In addition, the interaction designers need a certain amount of knowledge of the development environment so that they do not inadvertently break the developer's code.
Solution: With the physical separation of the View and ViewModel in MVVM, designers and developers can work more independently and safely. The designers are only working with the View (XAML), and the developers are only working with the ViewModel and Model which are separate code files (.cs or .vb). If the code-behind the XAML is eliminated (or at least minimized), then there is less risk that a designer could accidentally break some developer code.
This does not mean that the designers and developers work in isolation. They still need constant communication. What the designer can do with the UI is dependent upon the properties and methods that are exposed by the developers. Collaboration is still vitally important, but MVVM will minimize the likelihood that they will need to edit the same source files at the same time.
One small caveat: in many of the development shops that we work in, there is no separate designer; we are both the designer and developer. But even if we are not able to take advantage of this particular solution, we can still get the advantages of the clear separation of concerns and ability to unit test the presentation layer.
Every Design Pattern has both benefits and costs. The MVVM pattern is no different.
Consequence: Added Complexity
MVVM adds complexity to the Presentation Layer of the application. This is an additional learning curve for the development team.
When given a choice between dumbing-down code or smartening-up developers, I will choose the latter. I always prefer to bring up the skill set of the development team. But there may be some scenarios where this is not possible due to time constraints (ever heard this one before?). We need to make sure that the developers on the team understand the pattern before we start using it extensively; otherwise, we may find folks who take short cuts to get things to work and end up breaking the pattern.
Consequence: Errors Move from Compile Time to Run Time
As we've seen, implementation of MVVM is made possible through the very flexible data binding that is available in the XAML world. One of the downsides is that incorrect data binding will not generate errors at compile time. These errors are moved to run time, and often the errors produce silent failures (simply an empty TextBox or other unexpected behavior). Unfortunately, unit testing will most likely not catch these errors either since we are testing our ViewModel and not the View (where the bindings are specified).
These can be difficult to debug. The best tip that I have is to make sure you have the Output window open in Visual Studio when debugging your application. If there is a failed data binding, it will show up in the Output window.
Weighing the Costs
What is important is that we understand the costs and determine that we are willing to accept those costs in order to receive the benefits. If we are aware of the negative consequences of the pattern, then we can take steps to mitigate them. Just like every other tool in the toolbox, we need to make sure that we are picking the right one for the job. If we are not getting the benefits of the pattern, then we are only getting the costs, and we should reconsider our approach.
What About Implementation?
We'll need to save implementation for another day. As we noted earlier, there are a number of frameworks that can assist us.
Microsoft Patterns & Practices offers Prism as guidance for creating XAML-based (WPF, Silverlight, Windows Phone) applications. This includes using MVVM for the presentation layer. Prism provides a number of classes and interfaces that help make the implementation of MVVM cleaner and more generic. This results in less boiler-plate and replicated code in the application. Of course, it is always possible to implement these abstractions and helpers yourself, but someone else has already done the hard work. There is also great documentation available, including Implementing the MVVM Pattern.
Another popular option is the MVVM Lite Toolkit. This toolkit puts an emphasis on what they call "blendability" -- the ability to open and edit the user interface in Expression Blend.
Paul Sheriff shows the MVVM pattern in action without using external frameworks: The Basics of MVVM. This has advantages of focusing on the pattern rather than the framework (as was mentioned earlier). But there are some things that are a bit harder to do. I've done some implementations using a similar method, and there are pros and cons as with everything else.
And there are plenty of other implementations as well. If we go back to our Christopher Alexander quote above, patterns describe a solution "in such a way that you can use this solution a million times over, without ever doing it the same way twice." This doesn't mean that we should re-invent the wheel; it just means that there are different types of wheels to choose from.
MVVM is a very important pattern in the XAML world. Much of the samples and guidance revolving around the latest implementations (Windows Phone 7 and Windows 8) use MVVM as the core presentation pattern. What is important is that we understand the pattern itself, the components that make up the solution, and why we are using the pattern. Once we have these things down, we can make better decisions when we are implementing MVVM in our own applications. And when we are using an MVVM framework, we have a strong foundation to ensure that we are using the framework appropriately and that we are adhering to the principles of the pattern itself.
I am currently working on an application that shows a simple implementation of MVVM. That code will be available in the near future.
Good one JeremyReplyDelete
Nice article but a coding example would be helpfulReplyDelete