Validating Business Rules in MVVM

I’ve always thought that raw data validation should occur in the data Model, while Business Rule validation should occur in the ViewModel.

For example, verifying that a UserName is no longer than X length long should occur in the data model, while verifying that the UserName is unique would occur in the ViewModel. The reason for this is that the User Model is simply a raw data object. It doesn’t contain any advanced functionality like database connectivity, or knowing about other User objects. It’s a selfish little thing which only cares about it’s own data.

Since I use IDataErrorInfo for my validation and like to expose the entire Model to the View from my ViewModel, this presents a problem. Using the above example, I could bind a TextBox to SelectedUser.UserName, and it would automatically show an ErrorTemplate if the string was too long, however it wouldn’t show an error template if the UserName already exists.

After some thought, I decided to add a Validation Delegate to my Models to solve this problem. This is a delegate which ViewModels can use to add Business Logic Validation to its Models.

In the above example, the UsersViewModel might look like this:

public class UsersViewModel
{
    // Keeping these generic to reduce code here, but they
    // should be full properties with PropertyChange notification
    public ObservableCollection<UserModel> UserCollection { get; set; }
    public UserModel SelectedUser { get; set; }

    public UsersViewModel()
    {
        UserCollection = DAL.GetAllUsers();

        // Add the validation delegate to the UserModels
        foreach(var user in UserCollection)
            user.AddValidationDelegate(ValidateUser);
    }

    // User Validation Delegate to verify UserName is unique
    private string ValidateUser(object sender, string propertyName)
    {
        if (propertyName == "UserName")
        {
            var user = (UserModel)sender;
            var existingCount = UserCollection.Count(p => 
                p.UserName == user.UserName && p.Id != user.Id);

            if (existingCount > 0)
                return "This username has already been taken";
        }
        return null;
    }
}

The actual implementation of my IDataErrorInfo on my Model class would look like the code below. It’s generic, so I usually put it into some kind of base class for my Models.


    #region IDataErrorInfo & Validation Members
    
    #region Validation Delegate
    
    public delegate string ValidationDelegate(
        object sender, string propertyName);
    
    private List<ValidationDelegate> _validationDelegates = new List<ValidationDelegate>();
    
    public void AddValidationDelegate(ValidationDelegate func)
    {
        _validationDelegates.Add(func);
    }

    public void RemoveValidationDelegate(ValidationDelegate func)
    {
        if (_validationDelegates.Contains(func))
            _validationDelegates.Remove(func);
    }
    
    #endregion // Validation Delegate
    
    #region IDataErrorInfo for binding errors
    
    string IDataErrorInfo.Error { get { return null; } }
    
    string IDataErrorInfo.this[string propertyName]
    {
        get { return this.GetValidationError(propertyName); }
    }
    
    public string GetValidationError(string propertyName)
    {
        string s = null;

        foreach (var func in _validationDelegates)
        {
            s = func(this, propertyName);
            if (s != null)
                return s;
        }
    
        return s;
    }
    
    #endregion // IDataErrorInfo for binding errors
    
    #endregion // IDataErrorInfo & Validation Members

The idea is that your Models should only contain raw data, therefore they should only validate raw data. This can include validating things like maximum lengths, required fields, and allowed characters. Business Logic, which includes business rules, should be validated in the ViewModel, and by exposing a Validation Delegate that they can subscribe to, this can happen.

11 Responses to Validating Business Rules in MVVM

  1. Uri London says:

    I think you are missing the property name (probably ‘UserCollection’) in line 5. Should be: public ObservableCollection UserCollection { get; set; }

    Good article. Thanks

  2. inva says:

    Hi, i came from your article “A Simple MVVM Example”, really good stuff. Input validation is the next item on my “WPF ToDo List”. Great blog, nice articles, keep on writing :)
    Regarding this article i found a nice article on codeproject also naming the validation topic, see: http://www.codeproject.com/Articles/15239/Validation-in-Windows-Presentation-Foundation

    • Rachel says:

      Thanks! :)

      I thought about writing something for basic validation with IDataErrorInfo, but it seems like there’s already so much good material out there so I doubt I’d add anything new. Perhaps I will one day anyways for completeness.

  3. Daniel says:

    I guess my perspective would have been to put as much validation in the Model as possible. In the app I’ve been writing, I’m expecting the Model to be accessed from several different ViewModels (and therefore several different Views as well). Thus, I would be facing a lot of validation code duplication if I put such validation code in the VM. Thoughts?

    • Rachel says:

      I’ve always thought of models as just dumb data objects, so they should only perform raw data validation, such as required values, string length, or min/max values. Any kind of business validation should be inside the ViewModel to keep the layers separated.

  4. Pravin says:

    Finally I found a blogger who still blogs about WPF :-)

    • Rachel says:

      I love WPF! It’s such a fun language to work in, and I really enjoy buildings applications in it. I didn’t realize there was a shortage of WPF bloggers :)

  5. Roger says:

    “Business Rule validation should occur in the ViewModel”

    I disagree with this because it suggests that your business rules only apply to data modified via the user interface.

    What if you have web services that allow other systems to interact with your model, bypassing the user interface altogether? Or batch processing? Or a set of unit tests that need to exercise the model and can’t interact with the UI?

    Your business rules belong in the model to ensure that your data is always in a valid state regardless of how it was manipulated.

    That’s not to say that the logic needs to reside in your “dumb” data objects. It could be a second layer within your model that controls access to your data objects — ensuring that your data is always in a state that doesn’t violate your business rules regardless of the source of these changes. Think of your model as BOTH the data and rules that govern it.

    The ViewModel should just be a neat package of the model objects that are needed for your view.

    • Rachel says:

      Hi Roger,

      You bring up a good point about other UI interfaces that use the Model other than ViewModels. At the time of writing this, I was thinking of simple MVVM applications with very few layers, however if I did have a larger application infrastructure then I would definitely be sure to check the business logic on whatever layer is responsible to actually saving the data to the database, such as a web service layer, in addition to the UI layer.

      I dislike putting business logic directly on the model object because the rules might be different depending on the context in which the model is used. For example, the same model might be used from an administrative screen as the one that is used from a user screen, and the business rules validating the data may be very different for the two scenarios.

      In a perfect world I’d like to validate the data from both the UI side and from the web service side, and the code posted here allows me to do that. This way I don’t have to wait for the web service call to validate the data, and I don’t have to rely on the UI side to validate data before saving. It all depends on how your application is structured :)

  6. Hari says:

    Roger’s point brings up an important abstraction called ServiceLayer. We used this in our project effectively.

    Whenever ViewModel needed access to the model, we routed it through ServiceLayer.

    We needed a concept called ServiceLayer for many different reasons

    For example, assume that when the user clicks on a button named “Save”, we need to save data in one table and send a mail. Somewhere in application, this sequence has to be specificed (call to data accesss layer to save, call to mailsender class to send a mail).

    So, we abstracted out things like this in Service Layer

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

Follow

Get every new post delivered to your Inbox.

Join 101 other followers

%d bloggers like this: