68

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.

JDeuker
  • 896
  • 1
  • 11
  • 17

5 Answers5

71

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.

Community
  • 1
  • 1
Jon
  • 428,835
  • 81
  • 738
  • 806
  • 2
    The Notification thing is always described as the ViewModel part, thats why I would avoid it in the Model. It feels like doing the same thing twice. – JDeuker May 02 '13 at 13:45
  • 1
    @J.D.: Sure, but it's either that or implementing services. Your call. – Jon May 02 '13 at 14:00
  • @Jon: +1 for "never do that". Developers accustomed to the N-tier model tend to forget it's OK to add WPF library references to the VM, to hand back complex object that require composition, like a FlowDocument. – Mike Christian Nov 06 '13 at 19:20
  • 1
    +1 some great ideas. If you have some observable state, do you think the observable should live in the service since it's updated by the service? In this case, the ViewModel would just "expose" this observable for the view controller to observe it correct? – Mitch Dart Jan 30 '20 at 12:21
14

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.

Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • Does the service use the Model or ViewModel Instances? – JDeuker May 02 '13 at 13:28
  • 1
    @J.D.: model - there is no dependency on the view layer from the service layer. You can think of the service layer as augmenting and protecting the integrity of the model layer. – Kent Boogaart May 02 '13 at 13:41
  • Wat I've found the reason to avoid `INotifyPropertyChanged` in the model, because it suggests it is fine to bind to it, but sometimes if you have batch processing of even modest amount of data it can cause poor performance as the UI is responding to the `PropertyChanged` but due to the amount of data it cannot update itself slows the application down massively. It is better in this case update the UI when e.g. data loading is finished – Piotr Golacki Mar 06 '23 at 14:07
5

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.

Bizhan
  • 16,157
  • 9
  • 63
  • 101
rollsch
  • 2,518
  • 4
  • 39
  • 65
2

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.

Shervan
  • 21
  • 2
-1

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

Rafael A. M. S.
  • 637
  • 1
  • 6
  • 27