Book Image

MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF

Book Image

MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF

Overview of this book

MVVM (Model View View Model) is a Microsoft best practices pattern for working in WPF and Silverlight that is highly recommended by both Microsoft and industry experts alike. This book will look at the reasons for the pattern still being slow to become an industry standard, addressing the pain points of MVVM. It will help Silverlight and WPF programmers get up and running quickly with this useful pattern.MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF will help you to choose the best MVVM approach for your project while giving you the tools, techniques, and confidence that you will need to succeed. Implementing MVVM can be a challenge, and this book will walk you through the main issues you will come across when using the pattern in real world enterprise applications.This book will help you to improve your WPF and Silverlight application design, allowing you to tackle the many challenges in creating presentation architectures for enterprise applications. You will be given examples that show the strengths and weaknesses of each of the major patterns. The book then dives into a full 3 tier enterprise implementation of MVVM and takes you through the various options available and trade-offs for each approach. During your journey you will see how to satisfy all the demands of modern WPF and Silverlight enterprise applications including scalability, testability, extensibility, and blendability.Complete your transition from ASP.NET and WinForms to Silverlight and WPF by embracing the new tools of these platforms, and the new design style that they allow for. MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF will get you up to speed and ready to take advantage of this powerful new presentation platform.
Table of Contents (21 chapters)
MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF
Credits
Foreword
About the Authors
About the Reviewer
www.PacktPub.com
Preface
MVVM Frameworks
Index

History of presentational patterns


In this section we will cover the history of presentational (or GUI) patterns. Presentational patterns have been around for over 30 years and a full coverage of all the various patterns is outside of the scope of this book. We will instead focus on two of the major trends that have emerged over the last 30 years and look at how those two trends eventually evolved to MVVM for Silverlight and WPF.

Note

If you are interested in learning more about the history of presentational patterns than what is covered here, then see Martin Fowler's article GUI Architectures (http://martinfowler.com/eaaDev/uiArchs.html).

Monolithic design

Enterprise applications deal with displaying, manipulating, and saving data. If we build enterprise applications with no design so that each GUI component is coupled all the way down to the data access code, then there are a lot of problems that can emerge.

This style of design is called monolithic and the following diagram shows the coupling that exists under monolithic designs:

The problems with monolithic design

In this section we will review the problems caused by the tight coupling and low cohesion found in monolithic designs.

Code maintenance

Looking at the previous screenshot if you assume that UI Widget1 and UI Widgetn are using the same business logic, then using a monolithic design will cause code duplication. Every time a change needs to be made to the business logic, it would need to be made in both places. This is the type of issue that is solved by SoC and one of the motivators for design paradigms like 3-tier which we will look at in the Layered design section later in this chapter.

Code structure

Not having the code structured into reusable components and well-organized layers makes things like sharing session state difficult under monolithic design. As you will see in the examples that follow, once we move to MVC and MVP, there are many benefits including:

  • The session state becomes much easier to manage and share

  • Code is easier to reuse

  • Code is well-organized and easier to understand and maintain

  • Code scales easier as you can build components into separate DLLs for distributed deployment

  • Code is more extensible as you can replace components to provide different behaviors

Code testability

Creating code that can be effectively tested with unit tests requires designing for testability. The monolithic approach poses several problems for code testability including:

  • Poor isolation of tests: One of the core principles of unit testing is isolation of the tests. You want your unit tests to test one scenario of one method of one class and not to test the dependencies. Following this principle makes your tests more valuable because when a test fails it's more likely that developers who didn't write the test but introduced the change that broke the test will fix the issue. This is because it will be very easy for the developer to determine what the problem was that broke the test because it's so isolated and clear in its purpose. A big part of getting return on investment from unit tests comes from making them easy for developers to use and avoid making your unit tests high maintenance. With high-maintenance unit tests the developers might just delete, disable, or comment out the test instead of fixing the problem, which makes the expense that was put into creating the test a waste.

  • Testing the UI is difficult: Using automated testing to test the UI is notoriously difficult. Monolithic design makes this problem worse as there is no separation between the UI and the rest of the layers of logic. One of the major contributors to the need of separated UI patterns is the desire to move as much logic as possible out of the UI and into separate testable components.

  • Poor code coverage: Code coverage refers to how much of your code is covered by unit tests. Generally speaking, the more code you have covered by tests, the more stability you will create in your development process, and the more benefits you will reap from your tests. High code coverage provides fewer bugs and quicker refactoring times. When you create a monolithic application, it affects your ability to achieve high code coverage levels, because you can't test the UI logic and the coupling between the various layers as it makes mocking dependencies difficult, prohibiting creation of unit tests.

    Note

    100 percent test coverage is not always the best level of coverage as too much coverage can make the code brittle to change and make the code high maintenance. My general rule of thumb is that I want to test the functionality that is defined by the public interface of the class under test. Testing internal details that could change can provide more inconvenience than benefit. However, this rule of thumb assumes that you have a good separation of concerns and have applied the Single Responsibility Principle to the design of your application. Single Responsibility Principle is part of the SOLID design principles and more details about SOLID are easily found online if needed.

Data service stub

We will be using a data service stub as part of our data layer to take the place of a real data service in our sample applications so that we can focus on presentation patterns and not on data access patterns and techniques.

Note

Data layer will be explained in the Layered design section later in this chapter.

Let's start by creating a new Class Library project called ProjectBilling.DataAccess in a solution called MVVM Survival Guide as shown in following screenshot:

Now delete the Class1.cs file that is created by default by the project template and add a new class called Project and add the following code to Project.cs:

namespace ProjectBilling.DataAccess
{
    public interface IProject
    {
        int ID { get; set; }
        string Name { get; set; }
        double Estimate { get; set; }
        double Actual { get; set; }
        void Update(IProject project);
    }
 
    public class Project : IProject
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public double Estimate { get; set; }
        public double Actual { get; set; }
        
        public void Update(IProject project)
        {
            Name = project.Name;
            Estimate = project.Estimate;
            Actual = project.Actual;
        }
    }
}

Note

There are certainly better options than using an interface with an update method to allow for updating data objects but this approach will allow us to keep the code in this chapter and the next concise and allow keep our focus on the topic at hand.

Project is a simple domain object (or business object) that stores the project name, estimated cost, and actual cost. It's implemented off an interface to provide more flexibility and better testability and it provides an update method to make it easy to update an instance's values.

Now we will create the data service stub that will return fake data for our various clients to consume so that we don't have to be concerned with data access patterns and techniques and can instead focus on presentation patterns. Add a class to the project called DataService and add the code that follows to DataService.cs.

This class exposes one method called GetProjects(), which creates three projects and then returns them as a IList<Project>. We have implemented our data service stub based on an interface to support dependency injection.

Note

Dependency injection is a pattern where a dependency is allowed to be specified by an external component instead of being created internally. This pattern will be covered in more detail in Chapter 6, Northwind—Hierarchical View Model and IoC.

using System.Collections.Generic;

namespace ProjectBilling.DataAccess
{
    public interface IDataService
    {
        IList<Project> GetProjects();
    }

    public class DataServiceStub : IDataService
    {
        public IList<Project> GetProjects()
        {
            List<Project> projects = new List<Project>()
                {
                    new Project()
                    {
                        ID = 0,
                        Name = "Halloway",
                        Estimate = 500
                    },
                    new Project()
                    {
                        ID = 1,
                        Name = "Jones",
                        Estimate = 1500
                    },
                    new Project()
                    {
                        ID = 2,
                        Name = "Smith",
                        Estimate = 2000
                    }
                };

            return projects;
        }
    }
}

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com . If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.

This will allow us the flexibility to provide different implementations depending on the context. In a unit test we can provide a testing fake (stub or mock), in blend we can return a stub that returns design-time data and at runtime we can provide a real data service that returns real data. We will look into all of these techniques and also the use of inversion of control frameworks that make this process easier later in this book.