The design is nothing groundbreaking. I’m just taking a little bit from here, a little bit from there, and wrapping it up into what I consider to be a very simple, yet powerful set of classes. Lets start with the idea of a set of reusable, common rule methods to check common business rules like verifying that a string field is required and must be a maximum of 30 characters long. Add in a few rules for testing other simple scalar types, and you’ve got the basics covered. We’ve all built this class at least once. We may as well call it the "Hello Rules" class, right?
Next, we need a place to keep track of what rules have been broken, so that we can display them to the user. I have, in the past, created strongly-typed collections for storing these results. In this design, I’ve adopted the use of Generic collections instead. I haven’t created a strongly-typed collection since sometime last year, and I’m finding that I like the use of Generics quite a bit. They can be very powerful, and eliminate the tedious repetitive coding, or code generation template editing that strongly-typed collections require.
Now we need to get the broken rules into the collection. A common design is to build your rule checking methods to accept this broken rules collection as a parameter. Essentially we are saying "If this rule doesn’t pass, here’s where to put your complaint." During validation, the entity calls a rule, implemented as a method in some common rules library class, passing it the value of the property being validated, a reference to our broken rules collection, and whatever other parameters the particular rule method requires.
I was intrigued by an older design of Rocky Lhotka’s in which the addition of the broken rules was handled by the collection rather than by the rule. In this design, the results of the rule were passed directly to the collection. This was the equivalent of saying "Remember this rule if it’s broken, otherwise forget it". Something about this arrangement interested me. Putting the collection in charge of doing the adding and removing seemed like a pretty good idea. If I remember correctly, it had something to do with preventing outside code from messing with the contents of the collection, but that didn’t matter to me. I just liked the aesthetic of putting the collection in charge of its own contents.
The externalization of the rules still bothered me, though. I wanted to design a system that was wrapped up in a tidy package, with fewer "moving parts" to deal with. I decided to combine the common rules into the collection class itself so that I could just tell the broken rules collection to evaluate a condition and add a rule to itself if it was broken. I liked this design so much that incorporated it directly into the framework I developed at the time.
I still like the general "shape" of that design, and have created a newer, generic-based implementation.