Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM and Repository Question

Tags:

mvvm

Let's say that I have two views in my app, MemberListView and MemberEditView. They are associated with their perspective viewModels, MemberListViewModel and MemberEditViewModel. The models speak to a repository class, MemberRepository, which has the CRUD methods for the member class.

In the MemberEditView form, I have several dropdowns that display thinkgs like Status (Active/Inactive/Pending), the members trade code etc. They are ObservableCollection objects in my viewModel and are bound to ComboBoxes on the view. Should the MemberRepository handle the gets for retrieving the lists of each to be displayed?

What if on the MemberEditView I have a grid that displays all the jobs that the member has had over the years. If the user doubleclicks one of the jobs, it calls a JobHistoryEditView to display the job Information and it has a JobHistoryViewModel. Should the MemberRepository take care of the JobHistory CRUD methods or should I have a separate JobHistory Repository?

like image 789
BillG Avatar asked Nov 06 '22 05:11

BillG


1 Answers

Most MVVM applications would have this architecture:

View -> ViewModel -> Model -> Repository

I have recently been espousing a variant:

View -> ViewModel <- Presenter -> Model -> Repository

(Where A -> B means "A knows about B", but B doesn't know about A.)

Notice that in both cases, the only thing that knows about the repository is the Model, not the ViewModel. Your model isn't just the domain entities, it also has to house the business logic. Obviously one of the user stories your business logic has to support is something I'll call a MemberEditTask:

public class MemberEditTask
{
    private readonly Member _member;

    public MemberEditTask(Member member, IRepository repository)
    {
        this._member = member;
        this.StatusChoices = repository.GetPossibleMemberStatuses(member);
    }

    public ReadOnlyCollection<MemberStatus> StatusChoices { get; private set; }

    public MemberStatus Status
    {
        get { return this._member.Status; }
        set
        {
            if(!this.StatusChoices.Contains(value)) 
            { 
                throw new ArgumentOutOfRangeException();
            }
            this._member.Status = value;
        }
    }
}

All of this logic belongs in your Model because the list of possible choices (and validating that one of those was actually chosen) is defined by business logic. You could also imagine some other thing consuming the MemberEditTask, like an automated process running on the server that edits a member in response to a file uploaded on an FTP server, or a background process (setting the status to Inactive after a certain amount of time). All of those things need to execute the same business rules, so it all has to be common (not in the ViewModel).

So given that class, the ViewModel class looks like this:

public class MemberEditViewModel : ViewModelBase
{
    private readonly MemberEditTask _task;

    public MemberEditViewModel(MemberEditTask task)
    {
        this._task = task;
    }

    public IEnumerable<MemberStatus> StatusChoices 
        { get { return this._task.StatusChoices; }

    public MemberStatus Status 
    {
        get { return this._task.Status; }
        set
        {
            this._task.Status = value;
            NotifyAllPropertiesChanged();
        }
    }
}

In this case, as a very simple convenience, just believe that NotifyAllPropertiesChanged is a protected method of ViewModelBase that uses reflection to raise a PropertyChanged event on all public properties of the ViewModel. :) That's overkill of course, but it drives at a more important point...

This is almost a silly example because in this case, MemberEditViewModel is unnecessary. If the View is the only one setting Status then there's really no need to raise the property changed event either! Of course in the real world, you will have more properties and there will be interactions. The reason for the ViewModel to exist is to notify consumers when its view-related properties change, which is something the Model doesn't do (and shouldn't in my opinion). (The ViewModel also has extra View-specific logic to support animations, etc.)

So back to your question... whether or not the MemberRepository is responsible for executing the gets of the statuses is irrelevant from the point of view of the ViewModel because the repository is a service used by the Model. The Model is a service used by the ViewModel. Make your Model of the task/workflow/process/whatever expose the list of status options.

Sorry if that was long-winded.

like image 105
Scott Whitlock Avatar answered Nov 28 '22 09:11

Scott Whitlock