0

This is a WPF application using MVVM design that connects to Services (Business logic layer) which manage Models, with an exception that some property set of the model is bound to some views directly. This means the model properties are updated without involving ViewModel in some cases and this view scheme cannot be changed because of specific integration limitations.

enter image description here

The requirement is that in many cases, the property change in model involves some business logic to be performed in the Service that in turn updates other properties of the model and updates(CRUD) other models in the service too.

The two approaches that I thought of,

  1. The model has Service reference. The property-set call performs the service operation. But, I do not think it is a proper design strategy to do so, what are the thoughts on this?

    class Model
    {
        public int Property1
        {
            get => _property1;
            set
            {
                _property1 = value;
                Service.PerformOperation();
                OnPropertyChanged();
            }
        }
    }
    
  2. Service subscribes to events of Models.

    class Service
    {
        private void ModelPropertyChanged(object sender, EventArgs e)
        {
            if (e.PropertyName == "Property1")
            {
                PerformOperation();
            }
        }
    }
    

What is the better design strategy recommended in this type of scenarios?

=========================
Based on the answer [@Robert Harvey]

Introduce a Service-Model Worker between Service and Model and set Model properties through the Service-Model Worker. This ensures loose-coupling and keeps the Model less Anemic.

class Model
{
    public Model(IServiceWorker serviceWorker)
    {
    }

    public int Property1
    {
        get => _property1;
        set
        {
            _serviceWorker.SetProperty(
                nameof(Property1),
                value,
                () => { _property1 = value; });
        }
    }
}

class ServiceWorker : IServiceWorker
{
    public void SetProperty(string propertyName, object value, Action setPropertyInternal)
    {
        switch (propertyName)
        {
            case nameof(_model.Property1):
                {
                    //preview property change 
                    //(validation and other cancellable checks)
                    IModelValidator.Validate(propertyName, value);

                    //Before property changed
                    Service.PerformOperation_BeforePropertyChanged();

                    //Set property
                    setPropertyInternal();

                    //On Property change
                    Service.PerformOperation();

                    _model.RaisePropertyChanged(propertyName);
                }
                break;
        }
    }
}
  • 2
    How do you define "better?" (note: please don't respond with "generally-accepted," "most popular," "best-practice" or similar terms) – Robert Harvey Sep 27 '17 at 14:43
  • @RobertHarvey, by "better", my expectation is model with less inter-dependencies and high cohesion, and service that handles property change in a centralised manner regardless of the property change initiated from View, ViewModel or Model. – A Bittersweet Life Sep 27 '17 at 15:07
  • See my answer below. – Robert Harvey Sep 27 '17 at 15:10
1

If you need the loosest coupling possible, simply add a class between the Model and the Service that subscribes to the events on the Model, takes a reference to the Service, and performs the necessary business operations on the Service based on the Model events fired.

  • by "subscribes to the events on the ViewModel", do you mean the events of the model change? – A Bittersweet Life Sep 27 '17 at 15:13
  • Sorry, I meant "Model." – Robert Harvey Sep 27 '17 at 15:14
  • Out of curiosity, why is the Model firing events? – Robert Harvey Sep 27 '17 at 15:15
  • classic notify PropertyChanged event – A Bittersweet Life Sep 27 '17 at 15:25
  • I've updated the question with an implementation based on your suggestion. I provided another re-direction for the setting the properties through this intermediate class with an intention of moving Model validation and other any business logic performed at the ViewModel to the domain/business layer. This modification enables me to provide a reusable validation framework of the Model to other layers (UI and others). Any thoughts on this? – A Bittersweet Life Sep 29 '17 at 10:08
1

According to the ModelView-ViewModel pattern, there shouldn't be this arrow between your View and the Model.

The View is just the reflection of your ViewModel. Your ViewModel must control all stuff. Example: you said that 'changes in the Model must cause...', but with MVVM and databinding, you should have changes handled by the ViewModel first. Then, your viewModel decides what to do, which is: (i) call the service; (ii) update the Model(s) appropriately.

Please, see the diagram from: https://msdn.microsoft.com/en-us/library/hh848246.aspx

In other words:

  • Handle inputs from user on your ViewModel;
  • Depending on what variable changed, call the Service;
  • (optional) do that processing wrapping it using BackgroundWorker to do it asynchrolously;
  • After it finishes, update the model accordingly;
  • ("optional") this processing is done using ICommand approach, so you could mock it and unit test your viewModel to check if all information was updated appropriately.
  • The arrow between the View and the Model cannot be removed because of integration limitations. From the answer, I infer that you too point that Model should be free from business logic. – A Bittersweet Life Sep 29 '17 at 10:13

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.