I’ve been discussing an architectural question with a colleague this week. The scenario includes a WPF application, Domain Driven Design and MVVM. The goal is a relatively “pure” domain model where the classes in the model do not depend on any UI specific or other external assemblies. They are pretty much pure “POCO” objects.
The question is whether it’s reasonable to have the domain model implement INotifyPropertyChanged and INotifyCollectionChanged (via ObservableCollection).
- INotifyPropertyChanged is defined in System.dll. It’s part of System.ComponentModel namespace.
- INotifyCollectionChanged and ObservableCollection are both in WindowsBase.dll. These are in System.Collections.Specialized and System.Collections.ObjectModel respectively.
My take… go for it! Implement INotifyPropertyChanged on the domain model.
We’ve discussed a number of considerations, options and alternatives. A couple of things to consider:
- WPF has very strong binding support. It works well with a number of different patterns. Each of these provide a way for the UI to keep in sync with the data.
- Raise “___Changed” events; one for each property
- Other legacy binding interfaces
- With MVVM, do we fully wrap the model behind view models, or do we allow the view to bind to model directly. If we fully wrap the model, then the ViewModel can intercept all of the changes and raise PropertyChanged accordingly. If the View does bind directly to portions of the model, when we may need to force the UI to rebind manually.
There are some cool options that could help… Wrap the domain model with a “decorator” that adds INotifyPropertyChanged support. Use ICustomTypeDescriptor to expose the properties of the model, but intercept the “setter” so that the decorator can raise PropertyChanged. WPF honors ICustomTypeDescriptor, which makes this a pretty slick idea. I definitely think this is cool.
Another approach might involve using a “dynamic proxy” to add a decorator automatically. While I haven’t looked into it yet, I’m wondering if this could be done with an AOP framework like Spring.NET. This approach would hide more of the complexity under the surface. Code would interact with something that looks and feels like the original object instance.
There are some challenges with the decorator approach that I’m still wresting through.
- Complexity – Adding this decorator adds complexity. It must implement the ICustomTypeDescriptor interface, which has quite a few methods. The logic inside uses reflection to return PropertyDescriptors that represent each of the properties on the model. To be complete, the decorator needs to traverse any of it’s child objects and collections so that they are also wrapped with this decorator. This is especially important if the Views bind directly to any portion of the domain model.
- Decentralized – If two different views create separate instances of the decorator over the same instance a domain model, then one of these views will not get notified when the other view (or ViewModel) changes values on the model. This means that the ViewModels need to implement an additional mechanism for change notification.
- Loss of Fidelity – One of the use cases that can happen with an object is a relationship between properties. A change in one property may result in changes to other properties. In these cases, the typical INotifyPropertyChanged logic will raise PropertyChanged multiple times; once for each property that was changed. In a decorated scenario, the decorator may not be aware that the model has this dependency between properties and will not raise PropertyChanged for the dependent properties. The work-around is to raise PropertyChanged for all properties, but this means that we loose a little bit of fidelity.
In my mind, taking a dependency on System.dll and WindowsBase.dll is acceptable for the simplicity and functionality that you get in return. Additionally, I think that the disruption caused by these interfaces is minimal. They don’t change the core behavior of the model or add any sort of constraints on implementation (as a base class might do). Instead, they "augment” the functionality.
The model is the single source of truth. Adding change notification on the model means that I have a single source of truth for change notification too. This is huge because it makes things simpler.
The jury is still out on this one. I think that either way is viable. Drop a comment if you have an opinion on the matter.