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:

public DanishPhoneNumber(int number)
{
    if (!DanishPhoneNumber.IsValid(number))
    {
        throw new ArgumentOutOfRangeException("number");
    }
    this.number = 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.


Comments

onof
I agree.

Too often i see domain objects implementing a lot of validation code. I think that most of validation logic must be out of domain objects.
2010-07-12 15:05 UTC
Arnis L
People are struggling with understanding what they are validating, where they put their validation but i kind a think that nature of validity itself is often forgotten, unexplored or misunderstood.

DanishPhoneNumber value can't be less than 112. In reality we are modeling - such a phone number just does not exist. So it makes sense to disallow existence of such an object and throw an error immediately.

But there might be cases when domain contains temporary domain object invalidity from specific viewpoint/s.

Consider good old cargo shipment domain from Blue book. Shipment object is invalid and shouldn't be shipped if there's no cargo to ship and yet such a shipment can exist because it's planned out gradually. In these kind of situations - it might make sense to use IDataErrorInfo interface.
2010-07-13 13:44 UTC
I would prefer to disagree :)

We must keep in mind that we are not modeling the real world, but rather the business logic that addresses the real world. In your example, that would be represented by a proper domain object that models that a shipment is still in the planning stage. Let's call this object PlannedShipment.

According to the domain model, PlannedShipment has its own invariants that it must protect, and the point still remains: PlannedShipment itself cannot be in an invalid state. However, PlannedShipment can't be shipped because it has not yet been promoted to a 'proper' Shipment. Such an API is safer because it makes it impossible to introduce errors of the kind where the code attempts to ship an invalid Shipment.
2010-07-13 14:58 UTC
I think it is a very interesting thought to make domain objects immutable. However, I’m very curious about the practical implications of this. For instance: are all your domain objects immutable? Do you create them by using constructors with many arguments (because some domain objects tend to have many properties? It gets really awkward when constructors have many (say more than 5) arguments. How do you deal with this? Which O/RM tool(s) are you using for this immutability and how are you achieving this. Some O/RM tools will probably be a bad pick in trying to implement this. How are you dealing with updating existing entities? Creating a new entity with the same id seems rather awkward and doesn't seem to communicate its intent very well IMO. I love to see some examples.

Thanks
2010-07-13 20:08 UTC
I never said that all Domain Objects should be immutable - I'm using the terminology from Domain-Driven Design that distinguishes between Entities and Value Objects.

A Value Object benefits very much from being immutable, so I always design them that way, but that doesn't mean that I make Entities immutable as well. I usually don't, although I'm sometimes toying with that idea.

In any case, if you have more than 4 or 5 fields in a class (no matter if you fill them through a constructor or via property setters), you most likely have a new class somewhere in there waiting to be set free. Clean Code makes a pretty good case of this. Once again, too many primitives in an Entity is a smell that the Single Responsibility Principle is violated.
2010-07-14 09:06 UTC
It's very uncommon that i disagree with you, but...

With your phone number example in mind, the validation should imho never be encapsulated in the domain object, but belong to a separate validation. When you put validation inside the constructor you will eventually break the . Of course we have to validate for null input if they will break the functionality, but I would never integrate range check etc. into the class it self.
2010-07-15 20:28 UTC
You'll have to define the range check somewhere. If you put it in an external class, I could repeat your argument there: "your DanishPhoneNumberRangeValidator isn't open for extensibility." Value Objects are intrinsically rather atomic, and not particularly composable, in scope.

However, consumers of those Value Objects need not be. While I didn't show it, DanishPhoneNumber could implement IPhoneNumber and all clients consume the interface. That would make DanishPhoneNumber a leaf of a composition while still keeping the architecture open for extensibility.

The point is to define each type so that their states are always consistent. Note that for input gatherers, invalid data is considered consistent in the scope of input gathering. That's where IDataErrorInfo makes sense :)
2010-07-15 21:13 UTC
Arnis L
In short - I just wanted to mention nature of validity itself.

Second thing I wanted to emphasize is about Your sentence of making sense - we should focus on technology we are using not only to be able to express ourselves, but to be aware (!) of how we are doing it and be able to adjust that.

Patterns, OOP, .NET, POCO and whatnot are tools only. IDataErrorInfo is a tool too. Therefore - if it feels natural to use it to express our domain model (while it's suboptimal cause of arguments You mentioned), there is nothing wrong with using it per se. An agreement that our domain model objects (in contrast to reality) can be invalid if it simplifies things greatly (think ActiveRecord) is a tool too.
2010-07-20 08:51 UTC
I think we can often construct examples where the opposite of our current stance makes sense. Still, I like all rules like the above because they should first and foremost make us stop and think about what we are doing. Once we've done that, we can forge ahead knowing that we made a conscious decision - no matter what we chose.

To me, internal consistency and the SRP is so important that I would feel more comfortable having IDataErrorInfo outside of domain objects, but there are no absolutes :)
2010-07-20 09:35 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or Google Plus, or somewhere else with a permalink. Ping me with the link, and I may add it as a comment.

Published

Monday, 12 July 2010 12:58:16 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!