Remember the 4 tenets of SOA? One of them is that Boundaries are explicit. When somebody sends data to your service it is just like when you cross an international border into another country. Just a couple of hours drive north of us in Redmond is the border crossing to Canada. When you cross into Canada or back into the United States you have to stop your car and the border agents do their job. Their job is to make sure that you have proper documentation and that you aren’t smuggling something (or someone) bad into the country.
Your service has to have a similar border checkpoint and it is at the trust boundary where data enters your “country”. At the boundary you have to validate the data before it gets down deep into your business logic or database in some invalid form. The question I want to focus on here is one of design. Where should the validation be done?
Download Sample Code – WCF Service Fault and Validation Example
For Workflow Services see endpoint.tv - WF4 Workflow Service Data Validation Design
Most of the time we tend to validate data on entry to the service method. In small applications this approach is manageable but suppose you have a number (call it a Foo) that you use in 15 different services and you always pass it as an integer. As you review the system you find that some services reject any negative Foo value while others reject any Foo value less than 1. Your refactoring instinct tells you that it would be a good idea to centralize the Foo validation logic so you don’t end up with a variety of different validation rules.
Take a look at this code. It works, but could it be better?
public bool SomeOperation(int foo)
{
if (foo < 0)
throw new FaultException("Invalid Foo");
}
return ProcessTheFoo(foo);
The problem with this code is that
Services have to consume and receive data. This data flows across the service boundary and therefore must be untrusted until validated. I’m proposing some new tenets for service orientation. These tenets describe validation rules. Validation rules are an expression that tells you if the data is valid or not.
In your system you have to validate the state of an object. The validation rules for object should be written once and only once. This makes your system more maintainable..
Service Oriented Applications consist of the service boundary and lower layers or business logic. What makes an object valid at one layer should be the same as what makes it valid at another layer. Lower layers of the system may use internal types which hold data in intermediate states that is not valid according to the rules. When this is the case you should think of these types as being fundamentally different than the type that the validation rules apply to.
If validation is called from internal service logic, validation rules should throw exceptions types appropriate for internal use such as ArgumentNullException or ArgumentOutOfRangeException. At the service boundary if you want to propagate the error to the sender these exceptions must be converted to FaultException or FaultException<TDetail>
If the sender and receiver are able and willing to accept the tighter coupling that comes from sharing assemblies, you can share validation rules between sender and receiver. If you share validation rules, the sharing should be limited to only the types exposed at the boundary
public interface IFoo { int Data { get; set; } string Text { get; set; } }
Sound complex? Sure… but it is one thing to build a simple example and quite another to show an architecture style that yields some significant benefits. Of course there are many ways to accomplish these goals – you might have a better way – if so, please share it with me.
Happy Coding!
Ron http://blogs.msdn.com/rjacobs Twitter: @ronljacobs