About Me

Training

Nothin But .Net Developer Bootcamp

Navigation

Search

Categories

On this page

Validation In The Domain Layer - Take One

Archive

Blogroll

 Agile Developer Venkat's Blog
 Ayende @ Blog
 B#
 Barry Gervin's Software Architecture Perspectives
 Boy Meets World
 Brad Abrams
 Canadian Developers
 Christopher Steen
 Claritude Software News
 Clemens Vasters: Enterprise Development and Alien Abductions
 Coding Horror
 Coding in an Igloo
 Dare Obasanjo aka Carnage4Life
 Darrell Norton's Blog [MVP]
 David Hayden [MVP C#]
 Don Box's Spoutlet
 Eric Gunnerson's C# Compendium
 EZWeb guy: Jeffrey Palermo [C# MVP]
 Fear and Loathing
 Generalities & Details: Adventures in the High-tech Underbelly
 Greg Young [MVP]
 Greg's Cool [Insert Clever Name] of the Day
 IanG on Tap
 Ingo Rammer's Weblog
 ISerializable - Roy Osherove's Blog
 James Kovacs' Weblog
 Jason Haley
 Jean-Luc David
 Jeremy D. Miller -- The Shade Tree Developer
 JetBrains .NET Tools Blog
 Jimmy Nilsson's weblog
 John Bristowe's Weblog
 John Papa [MVP C#]
 Jon Skeet's Coding Blog
 JonGalloway.ToString()
 Jump the Fence or Walk Around
 Lambda the Ultimate - Programming Languages Weblog
 Larkware News
 Lutz Roeder
 Marquee de Sells: Chris's insight outlet
 Martin Fowler's Bliki
 Mike Nichols - SonOfNun Technology
 MSDN Magazine - .NET Matters
 MSDN Magazine - All Articles
 OdeToCode Blogs
 Onion Blog
 Planet TW
 Raymond Lewallen [MVP]
 Rockford Lhotka
 RodMan's Corner
 Roger Johansson's blog
 Sahil Malik - blah.winsmarts.com
 Sam Gentile's Blog
 Scott Bellware [MVP]
 Scott Hanselman's Computer Zen
 ScottGu's Blog
 secretGeek
 Service Station, by Aaron Skonnard
 Signum sine tinnitu--by Guy Kawasaki
 Stephen Toub
 Steve Eichert's Blog
 Steven Rockarts
 The Blog Ride
 The Coding Hillbilly
 The Daily WTF
 TheServerSide.net: News
 Tim Gifford
 Vance Morrison's Weblog
 you've been HAACKED

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 369
This Year: 94
This Month: 11
This Week: 0
Comments: 973

 Friday, May 19, 2006
Friday, May 19, 2006 1:45:19 PM (Mountain Standard Time, UTC-07:00) ( .Net 2.0 | C# )

I have been receiving a lot of email's centered around the topic of validation in the domain layer. If you are a developer who is trying to utilize a rich domain layer in your applications, then you might have struggled with the whole issue of validation. Conceptually, most of us know how to perform validation using some predefined set of business rules. Unfortunately, the way the validation is implemented often leaves us with a solution where validation is scattered haphazardly throughout multiple layers in the application. Worse yet, is the case where validation is duplicated in multiple layers in the application, and changing a rule entails changing the code in multiple places. To demonstrate one solution to this issue I am going to focus on building a simple voting application. Some of the business rules are as follows:

  • Must be between the age of 18 – 75 to vote (yes there is an upper limit!!)
  • Must live in the country the candidate is running for

Ok, so my rules about the voting process are a little weird to say the least, but hey, it’s just an example. As well as the rules for voting, upon submitting a vote the person voting has to supply all of the required voter information:

  • FirstName
  • LastName
  • Age
  • Address
  • Gender
  • CountryOfResidence

Take a look at the class diagram for the initial domain model:

InitialClassDiagram

As you can see. This is a pretty simple domain. Let’s switch back to validating the simple properties for now. Let’s start with the strings (firstname,lastname,address). Remember, I am going to be performing all of my validation in the domain layer, so the buck stops here. I am not trusting anything that may have come from the UI. I am going to write a test to capture the validation I want to perform against the first name of a person:

 

[Test]
public void ShouldValidateUsingRule()
{
IBusinessRule
<Person> firstNameRule = new BusinessRule<Person>(delegate(Person person)
{
return string.IsNullOrEmpty(person.FirstName);
});

Person personWithInvalidFirstName
= new Person("", "", "", 0, null, null);
Person personWithValidFirstName
= new Person("JP", "", "", 0, null, null);

Assert.IsTrue(firstNameRule.IsBrokenBy(personWithInvalidFirstName));
Assert.IsFalse(firstNameRule.IsBrokenBy(personWithValidFirstName));


}

As you can see from this test. I am trying to encapsulate the simple business rule “A person must have a non-null firstname” into a full fledged BusinessRule object instance. You will notice the use of the Predicate delegate that I plan on passing into the constructor of the BusinessRule class. This is the method that will perform the validation against the person. The implementation of this class should prove to be fairly simplistic:

 

public class BusinessRule<T> : IBusinessRule<T> { Predicate<T> brokenPredicate;

public BusinessRule(Predicate<T> brokenPredicate)
{
this.brokenPredicate = brokenPredicate;
}

public bool IsBrokenBy(T item)
{
return brokenPredicate(item);
}
}

Notice how I am making use of generics so that I can use the BusinessRule to work with any type. When I instantiate the BusinessRule for a certain type it will also constrain the type for the Predicate. If you take a look at the IBusinessRule<T> interface. You will notice that there is not much to it:

 

public interface IBusinessRule<T> { bool IsBrokenBy(T item);
}
 
 
 

I now want a way to encapsulate a set of rules that need to be checked against an entity. Let’s take a look at another test:

 

[Test]
public void ShouldRetrieveAllRulesBrokenByItem()
{
IBusinessRule
<Person> mockRule1 = mockery.CreateMock<IBusinessRule<Person>>();
IBusinessRule
<Person> mockRule2 = mockery.CreateMock<IBusinessRule<Person>>();
IBusinessRule
<Person> mockRule3 = mockery.CreateMock<IBusinessRule<Person>>();

Person person
= new Person("", "", "", 0, null, null);
Expect.Call(mockRule1.IsBrokenBy(person)).Return(
true);
Expect.Call(mockRule2.IsBrokenBy(person)).Return(
true);
Expect.Call(mockRule3.IsBrokenBy(person)).Return(
false);


mockery.ReplayAll();

IBusinessRuleSet
<Person> ruleSet = new BusinessRuleSet<Person>(mockRule1, mockRule2, mockRule3);
IBusinessRuleSet
<Person> brokenRules = ruleSet.BrokenBy(person);
Assert.AreEqual(
2,brokenRules.Count);
}

 

Once again, I am making use of RhinoMocks to mock out the business rules. As you can see, you can ask a BusinessRuleSet for all of the rules that are broken by an item. With the test in place, the implementation becomes a breeze:

 

public class BusinessRuleSet<T> : IBusinessRuleSet<T> { private IList<IBusinessRule<T>> rules;

public BusinessRuleSet(params IBusinessRule<T>[] rules) : this(new List<IBusinessRule<T>>(rules))
{
}

public BusinessRuleSet(IList<IBusinessRule<T>> rules)
{
this.rules = rules;
}

public IBusinessRuleSet<T> BrokenBy(T item)
{
IList
<IBusinessRule<T>> brokenRules = new List<IBusinessRule<T>>();

foreach (IBusinessRule<T> rule in rules)
{
if (rule.IsBrokenBy(item))
{
brokenRules.Add(rule);
}
}
return new BusinessRuleSet<T>(brokenRules);
}

public int Count
{
get { return rules.Count; }
}
}

With the business rule set in place let’s turn our attention to how we could go about asking domain objects to validate themselves:

 

 

[Test]
public void ShouldValidateUsingBusinessRuleSet()
{
IBusinessRuleSet
<Person> mockRuleSet = mockery.CreateMock<IBusinessRuleSet<Person>>();
IBusinessRuleSet
<Person> mockBrokenRules = mockery.CreateMock<IBusinessRuleSet<Person>>();

Person person
= new Person("", "", "", 0, null, null,mockRuleSet);

Expect.Call(mockRuleSet.BrokenBy(person)).Return(mockBrokenRules);
mockery.ReplayAll();

IBusinessRuleSet
<Person> brokenRules = person.Validate();
Assert.AreEqual(mockBrokenRules,brokenRules);
}

 

Being a big fan of dependency injection, I am making the decision that domain objects will be constructed with the rules they can use to validate themselves. This is especially handy in situations where the validation is context sensitive ie. In migration you often relax validation rules. I have decided that I will be able to ask a domain object to validate itself, and the result of this call will be a IBusinessRuleSet<T> containing all of the broken rules. With what we already have in place this should be fairly trivial:

 

public Person(string firstName, string lastName, string address, int age, Country countryOfResidence, Gender gender, IBusinessRuleSet<Person> ruleSet)
{
this.firstName = firstName;
this.lastName = lastName;
this.address = address;
this.age = age;
this.countryOfResidence = countryOfResidence;
this.gender = gender;
this.ruleSet = ruleSet;
}

public IBusinessRuleSet<Person> Validate()
{
return ruleSet.BrokenBy(this);
}

 

That’s it. In the constructor for the Person class I pass in the rule set that it can use for validation, and the Validate method becomes a simple one line delegation. The solution I have used to encapsulate the business rules can easily allow me to utilize the flyweight pattern to share RuleSet’s between multiple instances of the same type. More on that later. Let’s look at the completed code required to validate a person class:

 

public class Person
{
private string firstName;
private string lastName;
private string address;
private int age;
private Country countryOfResidence;
private Gender gender;
private readonly IBusinessRuleSet<Person> ruleSet;

public Person(string firstName, string lastName, string address, int age, Country countryOfResidence, Gender gender):this(firstName,lastName,address,age,countryOfResidence,gender,Rules.Default)
{

}

public Person(string firstName, string lastName, string address, int age, Country countryOfResidence, Gender gender, IBusinessRuleSet<Person> ruleSet)
{
this.firstName = firstName;
this.lastName = lastName;
this.address = address;
this.age = age;
this.countryOfResidence = countryOfResidence;
this.gender = gender;
this.ruleSet = ruleSet;
}

public IBusinessRuleSet<Person> Validate()
{
return ruleSet.BrokenBy(this);
}


public string FirstName
{
get { return firstName; }
}

public string LastName
{
get { return lastName; }
}

public string Address
{
get { return address; }
}

public int Age
{
get { return age; }
}

public Country CountryOfResidence
{
get { return countryOfResidence; }
}

public Gender Gender
{
get { return gender; }
}

public sealed class Rules
{
private Rules()
{

}

public static IBusinessRule<Person> FirstName
{
get { return new BusinessRule<Person>(delegate(Person person)
{
return ! string.IsNullOrEmpty(person.FirstName);
});
}
}

public static IBusinessRule<Person> LastName
{
get { return new BusinessRule<Person>(delegate(Person person)
{
return ! string.IsNullOrEmpty(person.LastName);
});
}
}

public static IBusinessRule<Person> Address
{
get { return new BusinessRule<Person>(delegate(Person person)
{
return ! string.IsNullOrEmpty(person.Address);
});
}
}

public static IBusinessRule<Person> Age
{
get { return new BusinessRule<Person>(delegate(Person person)
{
return person.Age > 0;
});
}
}

public static IBusinessRule<Person> Country
{
get { return new BusinessRule<Person>(delegate(Person person)
{
return person.CountryOfResidence != null;
});
}
}


public static IBusinessRule<Person> Gender
{
get { return new BusinessRule<Person>(delegate(Person person)
{
return person.Gender != null;
});
}
}


public static IBusinessRuleSet<Person> Default
{
get { return new BusinessRuleSet<Person>(FirstName, LastName, Age, Address, Country, Gender);
}
}
}
}
 
 

Notice how I am making use of the nested rules class inside of Person to place all of the distinct business rules for a person inside of the Person class itself. This is for demonstration, and to not go to the full blown effort of creating a RuleRegistry or the like. Notice how the constructor for the Person class that does not take a rule set, calls into the greedy constructor passing in the Default rule set, which is composed of each of the individual business rules. Let’s write some quick tests to verify that the default validation actually works:

 

[Test]
public void ShouldValidateFirstNameUsingDefaultRules()
{
Person person
= new Person("", "JP", "Address", 1, Country.CANADA, Gender.MALE);
IBusinessRuleSet
<Person> broken = person.Validate();
Assert.AreEqual(
1,broken.Count);
Assert.IsTrue(broken.Contains(Person.Rules.FirstName));
}

Currently the test will not compile because the IBusinessRuleSet interface does not provide the contains method. I need to implement the method (which I won’t bother showing) and then I can run the test. Once I have added the necessary code I will still get a failure because the object references for the 2 rules are not the same, even though in reality the rules are identical. To circumvent this issue we can add a custom equals implementation for the BusinessRule class. To make it simpler, I am going to add a new field to the business rule class to store the name of the business rule:

 

 

[Test]
public void BusinessRulesForATypeWithTheSameNameShouldBeEqual()
{
IBusinessRule
<Person> firstNameRule = new BusinessRule<Person>("FirstName",delegate(Person person)
{
return false;
});

IBusinessRule
<Person> firstNameRule2 = new BusinessRule<Person>("FirstName",delegate(Person person)
{
return false;
});

Assert.IsTrue(firstNameRule.Equals(firstNameRule2));
}

Initially this test will fail, this is because I need to add a custom equals implementation for the BusinessRule class:

 

public override bool Equals(object obj)
{
IBusinessRule
<T> other = obj as IBusinessRule<T>;
return (other == null ? true : this.Name.Equals(other.Name));
}

public override int GetHashCode()
{
return brokenPredicate.GetHashCode() + 29*name.GetHashCode();
}

With this code in place the equals method will now work. Of course, with this refactoring I need to go back to the Rules class inside of person, and change all of the rules so a name is passed in also. An example of one such rule change is as follows:

 

public static IBusinessRule<Person> Country
{
get { return new BusinessRule<Person>(MethodInfo.GetCurrentMethod().Name,delegate(Person person)
{
return person.CountryOfResidence != null;
});
}
}

Notice, in this initial incarnation I am using the Name of the property itself to determine the rule name!! Ok, this post is quickly turning into an essay, so I am going to cover one more point and then carry on another day. Take a look at the following rules in the Person class:

 

 

            public static IBusinessRule FirstName
{
get
{
return new BusinessRule(MethodInfo.GetCurrentMethod().Name,delegate(Person person)
{
return ! string.IsNullOrEmpty(person.FirstName);
});
}
}

public static IBusinessRule LastName
{
get
{
return new BusinessRule(MethodInfo.GetCurrentMethod().Name,delegate(Person person)
{
return ! string.IsNullOrEmpty(person.LastName);
});
}
}

public static IBusinessRule Address
{
get
{
return new BusinessRule(MethodInfo.GetCurrentMethod().Name,delegate(Person person)
{
return ! string.IsNullOrEmpty(person.Address);
});
}
}

You will notice that I am making use of the same method call in each argument string.IsNullOrEmpty. Unfortunately, what happens for any one of those checks when the string for any one of them is " ". The IsNullOrEmpty method will not catch this scenario. I need to fix the string checks so that they all behave the same. So now I have three pieces of code to change. Let me show you a way to encapsulate the checks:

public interface ISpecification<T>
{
bool IsSatisfiedBy(T item);
}

public class NonEmptyStringSpecification : ISpecification<string> { public bool IsSatisfiedBy(string item)
{
return (! string.IsNullOrEmpty(item)) && item.Trim().Length > 0;
}
}

I am making use of a toned down Specification pattern. If you are not familiar with that pattern, go ahead and pick up a copy of Eric Evans Domain Driven Design book. I promise I will devote a separate post to the Specification pattern (if people remind me!!). With the NonEmptyStringSpecificaton in place I can change the code in the Person rules class to the following:

 

public static IBusinessRule<Person> FirstName
{
get { return new BusinessRule<Person>(MethodInfo.GetCurrentMethod().Name,delegate(Person person)
{
return new NonEmptyStringSpecification().IsSatisfiedBy(person.FirstName);
});
}
}

public static IBusinessRule<Person> LastName
{
get { return new BusinessRule<Person>(MethodInfo.GetCurrentMethod().Name,delegate(Person person)
{
return new NonEmptyStringSpecification().IsSatisfiedBy(person.LastName);
});
}
}

Notice that because the check for a Non-Empty string is now encapsulated in “another object”, if the rules for a non empty string change, I now only need to change one place!! I could carry on and add a NonNullObject specification that would encapsulate the null checks, this would be added solely for consistency and readability.

Ok, so we now have a faily flexible validation engine in place, we have one problem. I can perform validation, but how am I going to get validation errors back to the client? Let’s write a quick test to prove out how I can go about this. In my head I think that it is going to take another change to the business rule class as well as the rule set:

 

 

[Test]
public void ShouldRetrieveMessagesForAllRules()
{
IBusinessRule
<Person> mockRule1 = mockery.CreateMock<IBusinessRule<Person>>();
IBusinessRule
<Person> mockRule2 = mockery.CreateMock<IBusinessRule<Person>>();

Expect.Call(mockRule1.Description).Return(FIRST_NAME_MESSAGE);
Expect.Call(mockRule2.Description).Return(LAST_NAME_MESSAGE);

mockery.ReplayAll();

IBusinessRuleSet
<Person> rules = new BusinessRuleSet<Person>(mockRule1, mockRule2);
IList
<string> ruleMessages = rules.Messages;

Assert.AreEqual(
2,rules.Count);
Assert.Contains(FIRST_NAME_MESSAGE,(IList)ruleMessages);
Assert.Contains(LAST_NAME_MESSAGE,(IList)ruleMessages);
}

The addition of a description for the rule requires me to add a new field and constructor to the BusinessRule class (the Description property gets added to the IBusinessRule interface):

 

 

public class BusinessRule<T> : IBusinessRule<T> { Predicate<T> brokenPredicate;
private string name;
private string description;

public BusinessRule(string name,string description,Predicate<T> brokenPredicate)
{
this.name = name;
this.description = description;
this.brokenPredicate = brokenPredicate;
}

public bool IsSatisfiedBy(T item)
{
return brokenPredicate(item);
}

public string Name
{
get { return name; }
}

public string Description
{
get { return description; }
}

public override bool Equals(object obj)
{
IBusinessRule
<T> other = obj as IBusinessRule<T>;
return (other == null ? true : this.Name.Equals(other.Name));
}

public override int GetHashCode()
{
return brokenPredicate.GetHashCode() + 29*name.GetHashCode();
}


}
 

With that change in place. I need to update the rules that I create in the person class to add a description (you can check that out in the actual code). The code needed to implement the Messages property on the BusinessRuleSet class becomes a snap (with the aid of another anonymous delegate):

 

public IList<string> Messages
{
get
{
return new List<IBusinessRule<T>>(rules).ConvertAll<string>(delegate(IBusinessRule<T> rule)
{
return rule.Description;
});
}
}

I make use of the ConvertAll method on the List class, to convert each of the rules into a plain old string, by passing in a delegate that just retrieves the Description from the rule!! Great. Everything is in place, let’s put all of this stuff through its paces. Take a look at the following WinForm:

 

PersonWinForm

This form will allow the user to enter in all of the basic information (minus Country and Gender). An accompanying presenter that can be consumed from this view is as follows:

 

 

public class PersonPresenter
{
private IPersonView view;

private IBusinessRuleSet<Person> rulesInContext = new BusinessRuleSet<Person>(Person.Rules.FirstName, Person.Rules.LastName, Person.Rules.Address, Person.Rules.Age);

public PersonPresenter(IPersonView view)
{
this.view = view;
}

public void Submit()
{
IBusinessRuleSet
<Person> broken = CreateFromView().Validate();
if (broken.Count > 0)
{
view.DisplayErrors(broken.Messages);
}
else { view.DisplaySuccess(); } } private Person CreateFromView()
{
int age = 0;
int.TryParse(view.Age, out age);

return ne