The "facets-enabled" meta model
During the course of my current project at work, I've been trying to come up with a way to describe the meta data of my domain model (which I dubbed the meta model). Because I've taking a rather ObjectSpaces-like approach to object-relational mapping, the domain schema I created seemed like a good place to store the facets of my class members(fields and/or properties). Here's an example of what it looks like:
<?xml version="1.0" encoding="UTF-8"?>
<DomainSchema AssemblyFullName="SomeProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
xmlns="http://www.seagile.com/domainschema-1.0/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.seagile.com/domainschema-1.0/
D:\WorkSpace\SomeProject\trunk\Schemas\DomainSchema.xsd">
<Classes>
<Class FullName="Seagile.SomeProject.SomeBaseClass" IdentityMember="ID">
<Members>
<Member Name="ID" Field="_id" Type="System.Guid"/>
</Members>
</Class>
<Class FullName="Seagile.SomeProject.SomeClass" BaseClass="Seagile.SomeProject.SomeBaseClass">
<Members>
<Member Name="Name" Field="_name" Type="System.String">
<Facets>
<MinLength>1</MinLength>
<MaxLength>5</MaxLength>
</Facets>
</Member>
<Member Name="ExternalCode" Property="ExternalCode" Type="System.String"
Required="False" Default="MCQ000">
<Facets>
<FixedLength>6</FixedLength>
<Pattern>^MCQ\d{3}$</Pattern>
</Facets>
</Member>
<RelationshipMember Name="Category" Field="_category" RelationshipType="One"
TargetClass="Seagile.SomeProject.SomeOtherClass"/>
<RelationshipMember Name="Materials" Field="_materials" RelationshipType="Many"
TargetClass="Seagile.SomeProject.Material"/>
</Members>
</Class>
<Class FullName="Seagile.SomeProject.Material" BaseClass="Seagile.SomeProject.SomeBaseClass">
<Members>
<Member Name="Name" Field="_name" Type="System.String"/>
<Member Name="TotalParts" Field="_totalParts" Type="System.Int32">
<Facets>
<MinInclusive>2</MinInclusive>
<MaxExclusive>11</MaxExclusive>
</Facets>
</Member>
<Member Name="BarCode" Field="_barCode" Type="Seagile.SomeOtherProject.BarCode, SomeOtherProject"
TypeConverter="Seagile.SomeOtherProject.Data.BarCodeConverter, SomeOtherProject.Data"/>
</Members>
</Class>
</Classes>
</DomainSchema>
By itself the schema is worth nothing, but in conjunction with a mapping and relational schema it's "easy" for a generic mapping layer to interpret how to retrieve and store objects. Strictly speaking the mapping layer doesn't need the type information and facets. But their presence is very much appreciated by the domain, application and presentation layers. The presentation layer can use the information to constrain the input fields, and both the domain and application layer can feed these facet values to validators (LengthValidator, RegularExpressionValidator, Int32RangeValidator, ...).
The xml representation is both a blessing and a curse. On one side, it's very easy to get up and running (using an xml schema to drive the intellisense and validation). It feels like a modelling tool if the schema is what you start out with. If the domain model is already in place it's a pesky task. Maintenance wise, however, it's a little devil. Having a validator to compare the declarative xml representation against an assembly is not a luxury. Keeping the code and xml in sync is asking for trouble. I think the best way to handle this is (a) to forget this whole idea, (b) have the xml representation generate the code or (c) have the code generate (attribute-based approach) the xml representation.
But lets not forget the problem I'm trying to solve, namely preventing the scattering of the domain's meta data. Where do you store the fact a column is 5 long, how do you constrain the textbox representing that same conceptual value to only take 5 characters to have an overal nice end-user experience, and how about duplicating that value once more in the domain layer so that other "clients" of your code play by the rules? How do you deal with change and know where to look if 5 becomes 10?