public class Person
{
public IList SpecialBirthPlaces;
public static readonly DateTime ImportantDate;
public String BirthPlace {get;set;}
I think the statements "I need to reflect this change in the user interface" and "It's an important domain rule which I need to capture" describe two different problems. Clearly, the first one needs to be solved; it isn't clear that the second one does.
If other parts of your domain model need to know about changes here, you would do well to have a look at Domain Events (for example, Udi Dahan's implementation). You could also use this to set the BirthPlace property when the BirthDate gets set, even asynchronously if it is a potentially lengthy operation.
Otherwise, let's just look at the UI issue. First of all, in my domain model, I would have each entity abstracted as an interface. If you don't, then you may need to at least make some properties virtual
. I'd also be using a layer of abstraction for generation/returning my entities, such as IoC/factory/repository. I consider this layer to be outside the bounds of the domain model itself.
Now, we need a mechanism to notify the UI of changes to properties in domain entities, but of course the domain model itself is in a sense a closed system: we don't want to introduce new members or behaviours to satisfy the needs of any outside concern.
What if we decorate the entity in question with an implementation that implements INotifyPropertyChanged
? We could do this in our repository, which we've established is outside the bounds of the domain, so we would not be modifying the domain model itself, only using composition to wrap the entities with functionality that the system outside the domain model needs. To restate, the recalculation of BirthPlace
remains a concern of the domain model, while the UI notification logic remains a concern outside of the domain model.
It would look something like this:
public class NotifyPerson : IPerson, INotifyPropertyChanged
{
readonly IPerson _inner;
public NotifyPerson(IPerson inner) // repository puts the "true" domain entity here
{
_inner = inner;
}
public DateTime BirthDate
{
set
{
if(value == _inner.BirthDate)
return;
var previousBirthPlace = BirthPlace;
_inner.BirthDate = value;
Notify("BirthDate");
if(BirthPlace != previousBirthPlace)
Notify("BirthPlace");
}
}
void Notify(string property)
{
var handler = PropertyChanged;
if(handler != null) handler(this, new PropertyChangedEventArgs(property));
}
}
If not using interfaces, you would simply inherit from Person
and override the BirthDate
property, calling members on base
instead of _inner
.