The necessity of a ViewModel class for each Model class

It is well known that you need a ViewModel if you want to add property change notification or validation to a Model. But what if the Model and ViewModel classes don’t differ at all? Should you create a ViewModel class if it has the same set of properties that the Model class has? The answer is yes, absolutely. The brief reasons are separation of layers and loose coupling. But I know that these abstract terms don’t sound very convincing so I’ll describe my point of view more particularly in this post.

As an example I’ll use this data source class (which can be either a web service, or a database, or a file, whatever) and this model:

public interface IModelSource
{
    SettingsModel GetSettings();
}

public class SettingsModel
{
    public string DefaultPage { get; set; }

    public string DownloadsFolder { get; set; }

    public bool AutoStart { get; set; }
}

So we should choose which approach to use.
Just to bind the model as it is:

SettingsModel model = source.GetSettings();
this.DataContext = model;

Or to use the extra ViewModel layer and convert the model before binding:

SettingsModel model = source.GetSettings();
SettingsViewModel viewModel = ConvertModelToViewModel(model);
this.DataContext = viewModel;

Let’s examine some cases and look at the differences between the approaches above.

 

1. Remove a property from the model class.
It happens quite often during development, this type of changes can be performed on the service side or in the database. So if to remove the AutoStart property, what will happens then?

The model-only approach
You will not be aware of the error and will not notice it until you run your application, navigate to the corresponding view and look at the Output window. There among messy messages you will see the notification about the binding error:
Runtime binding error
Also you should run your application in the Debug mode. If you start it by using the Ctrl+F5 combination, you will never find the error.

The viewmodel approach
You will be notified about the error almost immediately before you run the application. And you know: the earlier the bug is found – the easier to fix it.

 

2. Add a property of a complicated data type to the model class.
For example, this property of the Enum type:

NumberFormatCulture NumberFormat { get; set; }

The model-only approach
It isn’t clear how to bind this enum to the ComboBox control (I think, you will not display such programming language related values as “EnglishUS” to end users, won’t you?), so you’ll search it on Google or StackOverflow and eventually you’ll choose either a hacky solution (which will work only for the particular case) or you’ll agree to use the ViewModel (and admit wasting the time).

The viewmodel approach
Just add a few lines to the mapping configuration which can be easily bound to the ComboBox:

//Mapper.CreateMap ... 
.ForMember(vm => vm.NumberFormats, option => option.UseValue(
    EnumExtensions.GetValues<NumberFormatCulture>().Select(TranslateEnum)))
.ForMember(vm => vm.SelectedNumberFormat, option => option.MapFrom(
    m => TranslateEnum(m.NumberFormat)))

 

3. Change the structure of the model class.
For example, if to change some property of the string type to the property of another model type:

public PageModel DefaultPage { get; set; }

public class PageModel
{
    public string Url { get; set; }
}

The model-only approach
Again, you will not notice the error until you run the application and look at the Ouput Window (see p.1). Then you should run over all the xaml-markup in the view and correct bindings. And of course launch the application once again to assure that everything is fine after the changes.

The viewmodel approach
Just fix one line of the code in the mapping configuration:

//Mapper.CreateMap ... 
.ForMember(vm => vm.DefaultPage, option => option.MapFrom(m => m.DefaultPage.Url))

I must say that in this case the mapper doesn’t perform compile-time checking, but you can configure it so that it throws an exception if you try to map from the object type to the string type. And exceptions are any easier to notice and understand than debug messages in the Output window.

 

4. Project structure and maintenability
It isn’t a big advantage of the viewmodel approach in comparison with the above described points, but anyway, it is much better to have a good project structure when each view has a corresponding view model with the same class name prefix. You will not search which model class is bound to, for example, the SettingsView class (which model class: UserSettingsModel, ServiceSettingsModel? I don’t remember so I’ll take some time to find it out). You’ll just open the folder with view models and immediately see the corresponding SettingsViewModel class.

 

The most popular exuse not to create the extra layer is because it allegedly takes much time. But if to calculate the exact amount, the statement will be far from the truth.
– Add the AutoMapper library by using the NuGet package manager – 20 second (this task is performed only once)
– Create a new ViewModel class and just copypaste all of the properties of the model – 30 seconds
– Configure the mapping by using code-snippets – 5 seconds per property.

So it’ll take something about 1-2 minutes to create a ViewModel and I can’t say that it is a “huge amount of time”. And this amount is nothing in comparison with the time wasted on the above described points if to use the wrong approach. Don’t afraid to spend few minutes now, it will save many hours in the future.

Advertisements

7 Responses to The necessity of a ViewModel class for each Model class

  1. Good article but I’d go one further than this and say that if your model and viewmodel don’t differ at all then your UI is very likely really poor. It’s unlikely that your view of something as a developer is completely congruent with the end-user’s perspective.

  2. Jonathan Allen says:

    1. No benefit. If someone changes the model the very next change is to change the shadow class to match.

    2. That’s why we have value converters. And besides, why did you create a model that doesn’t match the excepted usage patterns?

    3. Same problem as #1, same solution as #2.

    4. If the view-model name matches the view name then its clear you aren’t creating reusable view-models. You are just moving the code behind to a less convient place. And of course mixing it up with the model data.

    Again this is a perfectly reasonable design pattern, but it is in no way MVVM. It is just classic forms over data, with all the associated problems that MVVM tries to correct.

    • vortexwolf says:

      1. The benefit of finding bugs earlier can’t be called “no benefit”. Also what does the “shadow class” term mean? Another name for ViewModel for those who want to call it differently and don’t like the term ViewModel?
      2. In my example I’ve tried to add the type which can’t be converted by using the IValueConverter interface. Maybe it is possible, but I don’t think so. As to the model class, it can’t be adjusted to the expected usage pattern because it can be situated in a database or be returned by a web service. And they can have many different clients written in different programming languages and many of them don’t need such properties as “ItemsSource” and “SelectedItem”.

      By the way, the PresentationModel pattern in the article http://martinfowler.com/eaaDev/PresentationModel.html is exactly the same what I do. You can look at diagrams and you will see that there is almost no difference between the model and the presentation model.

  3. Grauenwolf says:

    I have to wonder what your completed application is going to look like.

    So we start with a View and a View-Model.

    Presumably you at least understand that the view-model is supposed to contain the entry-point to external service calls such as “Open”. (It doesn’t matter if a database, file system, or WCF service honors the call.)

    A DTO of some sort comes back. (Maybe a WCF data object, maybe a ADO.NET data reader, again it doesn’t matter.)

    You translate the DTO into a proper Model object with all the business rules, validation, etc.

    Then for reasons I still don’t understand you read all the properties out of the model and shove them into the same View-Model that exposed the Open command. This is exactly the kind of mixing of concerns that the MVVM pattern is meant to avoid.

    XXX

    And sure, auto-mapping from the Model to the View-Model doesn’t take much effort. But then you also have to port all the Model’s logic into the View-Model.

    And if the Model has child-objects then what? Do you create a whole object graph of View-Models that mirror the Model’s object graph?

    • vortexwolf says:

      The service is called `IModelSource`. The DTO/Entity class is called `SettingsModel`. In this example nor model, nor view model has business rules, validation or logic. Just a set of properties which will be bound directly to the view and may be extended later.
      Anyway, in the future business rules will be situated in the ViewModel class and not in the Model/DTO/Entity class. Really, I haven’t ever seen a logic in a model class.
       
      If the model returned by the service contains a collection of child models, I will create the collection of child ViewModels too as a part of the Anti Corruption Layer concept.
      I’ll quote another article:
      “Even if the model we are using is being defined by an external subsystem I think it still makes sense to have an anti corruption layer, no matter how thin, to restrict any future changes we need to make in our code as a result of external system changes to that layer.”.

  4. Grauenwolf says:

    > – Create a new ViewModel class and just copypaste all of the properties of the model – 30 seconds

    One final comment. Do you really want to protray yourself as the guy who actively advocates for cop-and-paste programming?

    • vortexwolf says:

      Once I asked myself the question “Why should I create all these DTO and presentation models when I can use database entity models directly?”. The answers “separation of layers” and “it isn’t difficult to create extra models” can’t convience everyone, but I think that copypasting is the lesser evil than tightly coupled applications.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: