MVVM: ViewModel and Business Logic Connection

C#WpfDesign PatternsMvvm

C# Problem Overview


After doing a few Projects using the MVVM Pattern, Im still struggling with the Role of the ViewModel:

What I did in the past: Using the Model only as a Data Container. Putting the Logic to manipulate the Data in the ViewModel. (Thats the Business Logic right?) Con: Logic is not reusable.

What I'm trying now: Keeping the ViewModel as thin as possible. Moving all Logic into the Model Layer. Only keeping presentation Logic in the ViewModel. Con: Makes UI Notification realy painful If Data is Changed inside the Model Layer.

So I will give you an Example to make it more clearer:

Scenario: Tool to Rename Files. Classes: File : Representing each File; Rule: Contains Logic how to Rename a File;

If Im following approach 1: Creating a ViewModel for File, Rule and the View -> RenamerViewModel. Putting all Logic in the RenamerViewModel: Containing a List of FileViewModel and RuleViewModel and the proceeding Logic. Easy and fast, but not reusable.

If Im following approach 2: Creating a new Model Class -> Renamer, which contains a List of File, Rule und the proceeding Logic to interate over each File and apply each Rule. Creating a Viewmodel for File, Rule and Renamer. Now the RenamerViewModel only contains an instance of Renamer Model, plus two ObservableCollections to wrap the File und Rule List of the Renamer. But the whole Logic is in the Renamer Model. So if the Renamer Model is triggered to manipulate some Data by Method Calls, the ViewModel has no Clue which Data is manipulated. Because the Model doesnt Contain any PropertyChange Notification and I will avoid that. So the Business and Presentation Logic is seperated, but this makes it hard to notify the UI.

C# Solutions


Solution 1 - C#

Putting business logic inside the viewmodel is a very bad way to do things, so I 'm going to quickly say never do that and move on to discussing the second option.

Putting the logic inside the model is much more reasonable and it's a fine starting approach. What are the drawbacks? Your question says

> So if the Renamer Model is triggered to manipulate some Data by Method > Calls, the ViewModel has no Clue which Data is manipulated. Because > the Model doesnt Contain any PropertyChange Notification and I will > avoid that.

Well, making your model implement INotifyPropertyChanged would certainly let you move on to better things. However, it's true that sometimes it is not possible to do that -- for example the model may be a partial class where properties are auto-generated by a tool and don't raise change notifications. That's unfortunate, but not the end of the world.

If you want to buy something then someone has to pay for it; if it's not the model that gives such notifications then you are left with only two choices:

  1. The viewmodel knows which operations on the model (possibly) cause changes and it updates its state after each such operation.
  2. Someone else knows which operations cause changes and they notify the viewmodel to update its state after the model it is wrapping changes.

The first option is again a bad idea, because in effect it is going back to putting "business logic" inside the viewmodel. Not as bad as putting all the business logic in the viewmodel, but still.

The second option is more promising (and unfortunately more work to implement):

  • Put part of your business logic in a separate class (a "service"). The service will implement all business operations you will want to perform by working with model instances as appropriate.
  • This means that the service knows when model properties may change (this is OK: model + service == business logic).
  • The service will provide notifications about changed models to all interested parties; your viewmodels will take a dependency on the service and receive these notifications (so they will know when "their" model has been updated).
  • Since the business operations are also implemented by the service, this remains very natural (e.g. when a command is invoked on the viewmodel the reaction is calling an appropriate method on the service; remember, the viewmodel itself does not know about the business logic).

For more information on such an implementation see also my answers here and here.

Solution 2 - C#

Both approaches are valid, but there is a third approach: implement a service between the model and VM layers. If you want to keep your models dumb, a service can provide a UI-agnostic middleman that can enforce your business rules in a re-usable fashion.

> Because the Model doesnt Contain any PropertyChange Notification and I will avoid that

Why are you avoiding this? Don't get me wrong, I tend to keep my models as dumb as possible, but implementing change notification in your model can sometimes be useful, and you take a dependency only on System.ComponentModel when you do. It's completely UI agnostic.

Solution 3 - C#

I do the following

  1. View with XAML view logic only

  2. ViewModel which handles click handlers and creating new view models. Handles routed events etc.

  3. Model which is my data container and business logic regarding validating the model data.

  4. Services which populate the model with data. Eg call a web server, load it from disk, save to disk etc. Depending on the example often both my model and service will implement IPropertyChanged. Or they may have event handlers instead.

Any complex application imo needs another layer. I call it model + service, view, viewmodel. The service abstracts your business logic and takes a model instance as a dependency or creates a model.

Solution 4 - C#

Seems to me a very high number of MVVM developers have a wrong definition of Business Logic. Like said, Service (I call it Application Logic) + Model (Business Entities, or DTOs) = Business Logic Any code that does any calculation using available data is part of Business Logic.

For example website bellow literally says "Tax Calculation Logic" goes in the ViewModel, which is totally wrong and is because of Business Logic definition confusion. enter image description here from: https://www.clariontech.com/blog/wpf-with-mvvm-easily-separate-ui-and-business-logic

One reason might be that when they google "Business Logic" the first result they see could be the definition provided by DB admins. Obviously [Database.]Business_Logic and [MVVM.]Business_Logic are 2 separate words with totally different meanings.

Unfortunately, even though the purpose of MVVM is clear and the reason for dependencies between the layer is explained, this confusion is starting to get more widespread.

Solution 5 - C#

You can also implement IDataErrorInfo on both: Model and ViewModel, but doing the validation only in Model, this will ease your way in implementing the business rules only at Model ...

Ex:

ViewModel:

...

private Person person;

...

string IDataErrorInfo.this[string propertyName]
{
    get
    {
        string error = (person as IDataErrorInfo)[propertyName];
        return error;
    }
}

Model:

public class Person:INotifyPropertyChanged,IDataErrorInfo
{

...

   string IDataErrorInfo.this[string propertyName]
   {
        get { return this.GetValidationError(propertyName); }
   }

...

   string GetValidationError(string propertyName)
   {
        if(propertyName == "PersonName")
             //do the validation here returning the string error
   }
}

Plus take a look at the MVCVM Pattern, im actually using it and it is pretty fine to abstract the business logic to a controller class instead of the model or viewmodel

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJDeukerView Question on Stackoverflow
Solution 1 - C#JonView Answer on Stackoverflow
Solution 2 - C#Kent BoogaartView Answer on Stackoverflow
Solution 3 - C#rollschView Answer on Stackoverflow
Solution 4 - C#ShervanView Answer on Stackoverflow
Solution 5 - C#Rafael A. M. S.View Answer on Stackoverflow