Thursday, March 23, 2006

Microsoft doesn't do Encapsulation.

Microsoft has a big problem with encapsulation. It's tools don't support it, it's advice goes against it and your average VB.NET programmer has little idea what it means. I'm currently working on an enterprise application, written in VB.NET and working alongside a team of VB.NET developers. Now, back in a past life, I too was a VB developer (although with a strong interest in Java and C++) and I wrote applications around recordsets, but over the last three years, since I started doing C#, I've been infected by reading lots of Martin Fowler, Eric Evans, Rocky Lhotka and the Domain-Driven-Development agile crowd. Now I want to write Domain-Driven object-oriented applications. I've managed to steer my current team away from datasets but I just can't get them to understand why encapsulation is good and that's mainly because Microsoft don't seem to get it either. So what do I mean? Take the XML Serializer that's used by web services. In order to serialize an object, it has to be written in a particular way. It has to have a default public constructor and all its properties have to be gettable and settable. Here's an example Person object that can be XML serialized:
class Person
{
 string _name;
 int _age;

 public Person()
 {
 }

 public string Name
 {
  get{ return _name; }
  set{ _name = value; }
 }

 public int Age
 {
  get{ return _age; }
  set{ _age = value; }
 }
}
So what's the problem? Well, say we want to have a business rule, such as 'a person's name must be at least have at least one character. How do we enforce this? Well we can write some validation on the Name property setter:
public string Name
{
 get{ return _name; }
 set
 {
  if(value == null) throw new ArgumentNullException();
  if(value.Length == 0) throw new ApplicationException("Name must be at least one character");
  _name = value; 
 }
}
But that doesn't stop a developer writing:
Person myPerson = new Person();
and never setting the Person property. Now you have an invalid person object floating around in your application. You've effectively given up the power that object oriented development gives you to create classes that enforce their rules. Business rules about a Person are now leaking into the rest of the application. Without a parameterised constructor, and read only properties, you have to start relying on convention; rules like: "you must not store a Person object to the database without first setting it's Name property". The object can't protect itself so the developer has to start remembering a ever increasing list of do's and don'ts. My mental stack usually overflows after a handfull of arbitary rules. When I suggested writing properly encapsulated business classes to my team I came up against three main sticking points: 1. You can't pass encapsualted objects over web services (our communications layer is a web service). 2. You can't use form data binding with encapsulated objects (how do you start the new Person form with a blank name?) 3. You can't restore an encapsulated object from the database. Although I can answer all three objections: 1. Web services aren't meant for internal application communication. That's what remoting is for. 2. See my ealier post on using a nested builder class to solve the form databinding issue. 3. You can use reflection to restore the internal state of an object. (Hey, but that's three times slower than a property access! Perfomance old chap!) It was a step to far. It's not the way that Microsoft suggests working in most of it's architectural publications and it's not the way that it's tools support. You tend to have to work around the default behaviour. The little that I've read about LINQ (and it is only a very little) suggests that it wont support persisting the internal state of an object either. I was very pleased to see Rocky Lhotka feels the same pain that I do. Update: I was wrong about DLinq. Of course I should have read a little more about it before commenting:) Today I read the DLinq overview. DLinq uses attributes on domain classes to specify the object relational mapping and those attributes support internal field access, so it can persist the external state of a domain object without invoking property accessor business rules. Actually reading about it made me slightly more enthusiastic. So long as they get around to implementing polymorphic types and many-to-many relationships, I could probably live with attributed domain objects, although it smells of ORMapping in the domian layer, something that I'm against, being a big fan of layered de-coupled architectures.

No comments: