MVVM: ViewModel and Business Logic Connection
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.
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:
- The viewmodel knows which operations on the model (possibly) cause changes and it updates its state after each such operation.
- 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.
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.
I do the following
View with XAML view logic only
ViewModel which handles click handlers and creating new view models. Handles routed events etc.
Model which is my data container and business logic regarding validating the model data.
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.