Detail Level in Objects Returned by a Service
On the drive home tonight I listened to this episode of Ron Jacobs' ARCast. In it, he talked with Denny Boynton about SOA, and some of the best- and worst-practices ("patterns and anti-patterns") associated with creating services.
One of the topics that came up is something I've thought a lot about in the past - how to tell the service what kind of details you want in the objects you're requesting.
For example, let's say you're calling off to a service to retrieve a list of customers. What do you need in that list? Obviously each customer should have a name; possibly an address; but do you really need their list of contacts, or outstanding invoices? If you're expecting to get a list containing 1000 customers, then no - you only want the bare-minimum of details back.
Ron suggested that in the real, "on-paper", world, you'd probably submit a "customer list request" form, and that form would have a series of checkboxes letting you specify the details you need. If you translate that idea to code, I guess you could define an enum which has a value for each property in your Customer class:
[Flags]
public enum CustomerProperties { Name, Address, Contacts, Invoices }
So now you could pass that to the service and it could populate the list of Customer objects accordingly.
This, to me, seems a bit too tightly-coupled. I was thinking about a way you could do this so that the calling program didn't have to worry too much about individual properties.
The idea I've come up with is a way to specify a "detail level" using custom attributes.
First, we define a new DetailLevelAttribute class:
[AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
class DetailLevelAttribute : Attribute
{
int level = 0;
public DetailLevelAttribute(int level)
{
this.level = level;
}
public int Level
{
get { return level; }
}
}
Now we can apply that attribute to all the properties in our class:
class Customer
{
[DetailLevel(0)]
public string Name
{
get; set;
}
[DetailLevel(0)]
public string Address
{
get; set;
}
[DetailLevel(1)]
public List<Invoice> Invoices { get { return _invoices; } }
}
Ok! So now we have a Customer class whose properties are each decorated with a DetailLevel attribute. Our service, then, can accept a "requiredDetailLevel" parameter, and using reflection, decide whether the property needs to be populated before returning the customer:
int requiredDetailLevel = 1;
Type custType = typeof(Customer);
Customer cust = new Customer();
if (IsPropertyRequired(custType, "Name", requiredDetailLevel))
{
// populate cust.Name
}
if (IsPropertyRequired(custType, "Address", requiredDetailLevel))
{
// populate cust.Address
}
if (IsPropertyRequired(custType, "Invoices", requiredDetailLevel))
{
// populate cust.Invoices
}
... which just leaves us to define the "IsPropertyRequired" method:
bool IsPropertyRequired(Type type, string propName, int maxDetail)
{
Type dlaType = typeof(DetailLevelAttribute);
System.Reflection.PropertyInfo prop = type.GetProperty(propName);
if (prop == null)
{
// Property not found. Maybe raise an exception.
return false;
}
object[] attrs = prop.GetCustomAttributes(dlaType, false);
if (attrs.Length == 0)
{
// Not decorated with DetailLevelAttribute. Assume required.
return true;
}
DetailLevelAttribute dla = attrs[0] as DetailLevelAttribute;
if (dla == null)
{
// Something's gone bananas. Maybe Assert.
return true;
}
return dla.Level <= maxDetail;
}
So now our service has a way to determine whether a property needs to be filled out, and it doesn't need to know too much about the object itself. The developer who designs the objects gets to specify which bits belong to which "detail level" using attributes.
Could this work? I dunno. But I thought it was worth documenting - let me know your thoughts!

Comments
# Ron Jacobs
3/06/2006 9:36 AM
Nice - I don't understand why having the enum in the first case would be too tightly coupled. After all the service is offering up a type (Customer) and in the request for customer you can specify which customer members you want. Both sides in the conversation have to understand the customer type and the enum doesn't seem to add a tight coupling to me.
The attribute detail level idea is cool though - on the downside it becomes a little less clear what the detail level means from the calling code. What do I get when I ask for detail level 2? On the other hand if I specify something like CustomerRequest.Detail = CustomerRequestOptions.Invoices | CustomerRequestOptions.Contacts then in the calling code it is more obvious what I am asking for.
# mabster
3/06/2006 9:14 PM
Hi Ron! Thanks for the comment.
I agree that from a code-readability point of view the enum is better.
One benefit I see from the "detail level" idea is that a change in the business object doesn't mean you have to change your calls to the service. If I suddenly introduce a new property of "Customer" I could set its detail level relatively high, and calling code would not need to retrieve it.
Like I said in the post, though, I have no idea how feasible this is. It was just an idea that sprang to mind after listening to your show, and I needed to get it out of my brain and onto the net! :)
# Benjy
5/06/2006 1:03 PM
The enum and the attribute sound good. i like the enum idea but the option of my code being able to dynamically change its behavior is also pretty appealing. As mentioned, without the necessary documentation, the 'requiredDetailLevel' does not tell you much.
Im not an SOA expert by any stretch of imagination but I was just thinking that since services need to be self describing, theres a lot of metadata that the service needs to expose to the consumer. So if you are considering adding more 'details' to the service which means that the 'requiredDetailLevel' attribute may contain more values than 1,2,3,4 in the future , perhaps you can consider adding an extra 'getDocumentation' method or some sort of custom policy which can be updated whenever needed without breaking the main method/contract. In this additional method/policy we could retrieve/show the list of enums and their meanings.
What do you think?
# mabster
5/06/2006 1:11 PM
Hi Benjy,
I think the enum would be self-documenting, but you're right - if you used the attribute then it would be nice to have some way to discover which fields have which detail-level.
Food for thought either way.