Question

I'm learning the MVVM pattern and something that comes up often is a duplication of data-types.

Say I have a Person datatype. Intuitively I want it to look like this:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public uint Age { get; set; }
}

But instead, the code will have a PersonViewModel a PersonModel class and in many cases there's also a PersonData class which is used for serialization.

A PersonViewModel inherits from a base class for ViewModels and the setters will have RaisePropertyChanged calls.

public class PersonViewModel: ViewModelBase
{
    private string _firstName;

    public string FirstName
    {
        get
        {
            return _firstName;
        }
        set
        {
            _firstName = value;
            OnPropertyChanged("Person");
        }
    }
    ...
}

A PersonModel does not call OnPropertyChanged and does not inherit from ViewModel. Also one of the reasons for this is to not bind the view to the model directly, rather to bind it to a ViewModel.

A PersonData will have the same properties but they'll be marked with the DataMember attribute and the class will have the DataContract attribute.

I have 2 questions:

1) Is it really necessary to have so many classes for the same data type? They usually have the exact same properties. Is it not better to just have one class per data type?

2) One of the problems is that changing one class requires you to change the other classes as well. I thought about using an interface that looks like this:

public interface IPerson
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public uint Age { get; set; }
}

and have the 3 classes implement the interface. If indeed all these classes are necessary is it a good solution for the consistency problem?

3) Is the distinction between "Model" and "ViewModel" really necessary in the data-types level? Sure I can understand why it's a good idea to have one unit of code be responsible for invoking commands, presenting data, etc... and have another unit of code be responsible for the business logic, but is it really necessary when we're dealing with data types that are being used across the entire codebase?

Was it helpful?

Solution

CRUD operations in MVVM do seem unnecessarily redundant, don't they? But the situation changes when actual business operations are involved.

Let's have a look at a different problem domain.

public class Invoice
{
    public Address BillingAddress { get; set; }
    public Address ShippingAddress { get; set; }
    public List<InvoiceItem> Items { get; set; }
    public double Shipping { get; set; }
    public double Tax { get; set; }
}

In this example Address, Invoice and InvoiceItem are all tables in the database, so you would need three classes to represent those. The fields exposed on the InvoiceViewModel would more closely resemble those of the class above.

Do you need all of these classes? Yes, you do. The whole point is to promote separation of concerns. The ViewModel should only expose those fields that are needed by the View. The View should not have to concern itself with what the database looks like; that is the job of the ViewModel and the underlying Model.

OTHER TIPS

DRY principle prescribes to avoid duplication of different pieces of knowledge, not just repeatable blocks of code. Two identical blocks of code can correspond to very different rules and that's totally fine to have such "duplication". In this specific case, data required by view and data required by persistence layer are two different pieces of knowledge and it is coincidental that they have similar or identical code.

100% CRUD app could benefit from binding directly to data entities bypassing view models layer but I think people fool themselves thinking that they write such apps. I have never seen 100% crud app, there's always at least some logic on top of that and it usually pays off to invest in CQRS and view models.

You shouldn't be afraid of not following DRY when it comes to data. Its actually a antipattern not to duplicate the data classes. Your read model should not be a 1:1 mapping to your database or datasource.

It should be mapped so that it supports the use case. So lets sayu your database entity is

public class Person 
{
   FirstName
   LastName
   SocialSecurityNumber
   Age,
   SomeListOfRelatedData
}

Lets say your use case is that you should cancel all subscriptions to people under 18 because of new regulations. They want a list of name and the persons age.

That view should be served a custom model only holding the data needed for this specific use case. This grows more important for larger and more complex systems.

Licensed under: CC-BY-SA with attribution
scroll top