Occasionally I get a question about whether it is reasonable or advisable to let domain objects implement IDataErrorInfo. In summary, my answer is that it’s not so much a question about whether it’s a leaky abstraction or not, but rather whether it makes sense at all. To me, it doesn’t.
Let us first consider the essence of the concept underlying IDataErrorInfo: It provides information about the validity of an object. More specifically, it provides error information when an object is in an invalid state.
This is really the crux of the matter. Domain Objects should be designed so that they cannot be put into invalid states. They should guarantee their invariants.
Let us return to the good old DanishPhoneNumber example. Instead of accepting or representing a Danish phone number as a string or integer, we model it as a Value Object that encapsulates the appropriate domain logic.
More specifically, the class’ constructor guarantees that you can’t create an invalid instance:
private readonly int number;
public DanishPhoneNumber(int number)
{
if ((number < 112) ||
(number > 99999999))
throw new ArgumentOutOfRangeException("number");
}
this.number = number;
Notice that the Guard Clause guarantees that you can’t create an instance with an invalid number, and the readonly keyword guarantees that you can’t change the value afterwards. Immutable types make it easier to protect a type’s invariants, but it is also possible with mutable types – you just need to place proper Guards in public setters and other mutators, as well as in the constructor.
In any case, whenever a Domain Object guarantees its invariants according to the correct domain logic it makes no sense for it to implement IDataErrorInfo; if it did, the implementation would be trivial, because there would never be an error to report.
Does this mean that IDataErrorInfo is a redundant interface? Not at all, but it is important to realize that it’s an Application Boundary concern instead of a Domain concern. At Application Boundaries, data entry errors will happen, and we must be able to cope with them appropriately; we don’t want the application to crash by passing unvalidated data to DanishPhoneNumber’s constructor.
Does this mean that we should duplicate domain logic at the Application Boundary? That should not be necessary. At first, we can apply a simple refactoring to the DanishPhoneNumber constructor:
if (!DanishPhoneNumber.IsValid(number))
public static bool IsValid(int number)
return (112 <= number)
&& (number <= 99999999);
We now have a public IsValid method we can use to implement an IDataErrorInfo at the Application Boundary. Next steps might be to add a TryParse method.
IDataErrorInfo implementations are often related to input forms in user interfaces. Instead of crashing the application or closing the form, we want to provide appropriate error messages to the user. We can use the Domain Object to provide validation logic, but the concern is completely different: we want the form to stay open until valid data has been entered. Not until all data is valid do we allow the creation of a Domain Object from that data.
In short, if you feel tempted to add IDataErrorInfo to a Domain Class, consider whether you aren’t about to violate the Single Responsibility Principle. In my opinion, this is the case, and you would be better off reconsidering the design.
Remember Me
a@href@title, b, em, i, strike, strong
Page rendered at Saturday, February 04, 2012 10:31:23 PM (Romance Standard Time, UTC+01:00)
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.