let’s say that in my application, there is an user interface for representing a (geographic) map. It is integrated into the application as a UserControl and has its view model behind it.
Now, suppose I want to provide other parts of my application with a generic service interface to perform common tasks on the map (zoom, pan etc.) and not worry about the UI specifics. I could give away direct reference to the viewmodel, but I am pretty sure I would violate separation of concerns principle, not to mention it would be less testable.
So there are few questions:
- Does it make sense and is it good practice to implement such services (which act as an intermediate link to the UI) in the first place?
- Since the service operates directly on the map’s viewmodel, should it be the viewmodel itself which implements the service interface?
- Is it appropriate for the service interface to provide events (e.g. besides providing a method to change the map scale, provide an event that the map scale was changed as well)? Or is it preferable to employ some kind of event broadcaster (aggregator) mechanism to push such notifications out of service interfaces?
Thanks in advance for your help.
Whenever you need two view models to communicate (or something similar, such as a button that wants to invoke a command on a view model other than its own), the best practice I’ve found is use a messaging channel. (MVVM Light has the Messenger class; Prism and Caliburn.Micro both have an EventAggregator.) When the target of the command is instantiated (your map view model), it will register for specific commands on a messaging channel. When an invoker (such as a button) is instantiated, it can then send commands over that same channel. This keeps your components loosely coupled. Commands from the messaging channel can be easily mocked for unit testing. It also opens up other avenues to you, such as having multiple maps open at the same time (simply use a different messaging channel or some sort of token).
I’d skip the whole service interface in your case. When using an event aggregator, it doesn’t really add much. Depending on the size and complexity of your code base, you might want to keep it around so it describes the commands available for a map, but that only makes sense if you have more than a handful of commands. In that case the service would register as the end point for commands on the messaging channel and would then have to forward those commands on to a map view model. (See? Doesn’t add much and only seems to complicate things.)
Skip the events. They don’t seem to add anything.