Late last year I made myself a promise that I would resuscitate my blog instead of just tweeting drivel. I don’t particularly feel up (or self important enough) to the old 10 page missives of yore, so I’m just going to write about the project and OSS work I’m doing this year and see if any of it is useful or interesting.
I’ve built software in some capacity for a dozen years. My first couple projects were exciting just because it was my first couple projects, but for the most part, I’ve always felt like everybody else worked on cooler projects than I did – until I started at Dovetail anyway. Yesterday I (mostly) wrapped up a crucial subsystem for us on our story for “extension properties that I thought was cool enough to share.
We know from experience that our customers need to add some of their own fields to track their own special data in the system. Here’s the facts, requirements, and assumptions we’ve made about these extension properties:
- We’re assuming that the extension properties will be completely set up by our professional services folks during the deployment product, so we don’t have to open up any magic way for the customers to change the database schema and screens on the fly.
- Our “professional services” guys sit 10 feet from me and they’re perfectly capable of some coding
- We really need an “Open / Closed” architecture. By this I mean that we absolutely must be able to add these customer specific properties to their own installation without any impact or change to our core codebase. Nothing destroys a small ISV like having to maintain customer specific
- The extension properties have to be editable on the screen
- The extension properties have to be available inside our rules engine
- I’d really, really like to reuse all our existing infrastructure for validation, persistence, and html conventions for these extension properties. I’m lazy and I don’t want to write a lot of special infrastructure.
Step 1: Adding Extends<T> to the Domain Model
My original thought was to just make the extension properties live in a name/value collection on all of our entities. It’s a simple solution and the quickie NHibernate spike I did made it clear that the persistence wouldn’t be a problem. However, we make use of a *lot* of conventions and validation policies in our application that utilize the type system and our validation attributes to drive behavior. Using the name/value approach meant that I threw all that away. Instead, I decided that the extension properties would just live in a separate class like this one that I use for testing:
// This object would be attached to our
// Site objects in our Domain Model
public class SiteExtensions : Extends<Site>
{
[Required, ShowNew]
public string ExtraString { get; set; }
public int ExtraNumber { get; set; }
public DateTime? ExtraDate { get; set; }
// Fake property and a fake list I use to test
// multi-level lists
[ListValue(“Year”), ShowNew]
public string Year { get; set; }
[ListValue(“Make”), ShowNew]
public string Make { get; set; }
[ListValue(“Model”), ShowNew]
public string Model { get; set; }
}
A couple notes about this class. As you see, it inherits from the Extends<TEntity> class where TEntity is the type in our domain model that is being extended. Extends<TEntity> is strictly a marker interface with no functionality.
public class Extends<T> where T : DomainEntity
{
}
You might also notice that the SiteExtensions class is decorated with several attributes (Required, ShowNew, ListValue, etc.). These attributes are mostly for our server side validation, but they also drive Html construction in the web pages and tie into client side validation with jQuery validation (more on this later).
Step 2: Discovering Extension Properties on Application Startup
So far, so good, but now I need to hook the SiteExtensions objects to Site objects while keeping the SiteExtensions class in a totally separate assembly specific to that particular customer. First, all of our domain model classes implement a layer supertype class called “DomainEntity,” so this becomes a natural way to connect our domain entities to their extension properties:
[Serializable]
public class DomainEntity : Entity, IValidated
{
public object ExtendedProperties { get; set; }
}
Next, I need to discover the proper extension property class for each domain model type (if any) at runtime. I decided to store that information in a simple static class called “ExtensionProperties” that acts as a well known place in the system to query for the proper type of extension class:
public static class ExtensionProperties
{
private static readonly Cache<Type, Type> _types = new Cache<Type, Type>();
public static void Register(Type entityType, Type extensionType)
{
_types[entityType] = extensionType;
}
public static void ClearAll()
{
_types.ClearAll();
}
public static bool HasExtensionFor(Type entityType)
{
return _types.Has(entityType);
}
public static Type ExtensionFor(Type type)
{
return _types[type];
}
}
Now that we’ve got ExtensionProperties, we need to discover the available extension properties at application startup and register them with ExtensionProperties. Unsurprisingly, I used StructureMap to discover the extension property types. First off, we’re doing customer extensions by placing their customizations in separate assemblies that are placed into our application bin. At the moment, our convention is that these assemblies must contain “Extensions” somewhere in their assembly name to be picked up. So knowing that assumption, I created an auto-registration policy in StructureMap that scans all the “Extensions” assemblies in our application base directory and discovers the Extends<T> types:
public class ExtensionRegistry : Registry
{
public ExtensionRegistry()
{
Scan(x =>
{
ExtensionProperties.ClearAll();
x.AssembliesFromApplicationBaseDirectory(assem => assem.GetName().Name.Contains(“Extensions”));
x.Convention<ExtensionScanner>();
});
}
}
// This little guy just finds any type that inherits from
// Extends<T> and registers that type against “T” in
// our wellknown ExtensionProperties class
public class ExtensionScanner : IRegistrationConvention
{
public void Process(Type type, Registry graph)
{
if (type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof (Extends<>))
{
var entityType = type.BaseType.GetGenericArguments()[0];
ExtensionProperties.Register(entityType, type);
}
}
}
Step 3: Persisting the little devils
The goal is definitely to make the properties on the extension classes persistent, and I’d really prefer not to write custom SQL for this one off need, so it’s time to teach NHibernate how to persist these extension types – if they exist. The Extends<T> objects are really just part of the domain entity that they extend, so let’s just say we put the backing fields directly onto the proper table for each entity. We *might* have a conflict with the extension properties and later versions of our product, so let’s make a little naming convention for these backing fields and say that all extension fields in the database have to be prepended with “x_” like this (yes kids, I *can* write Sql by hand if I absolutely have to):
BEGIN TRANSACTION
GO
ALTER TABLE dbo.Site ADD
x_ExtraString nvarchar(50) NULL,
x_ExtraNumber int NULL,
x_ExtraDate datetime NULL,
x_Year nvarchar(20) NULL,
x_Make nvarchar(50) NULL,
x_Model nvarchar(100) NULL
COMMIT
Now I need to alter our NHibernate mappings *if* a domain entity has an extension type attached to it. Back in the bad old HBM.XML days this used to be a klooge, but Fluent NHibernate (FNH) made it very simple. Let’s just treat the Extends<T> type as a “Component” on the main entity type. We have our own base class that extends FNH’s ClassMap<T> called DomainMap<T> to specify NHibernate mappings with our own policies, so let’s just add a bit of code to that base class that will dynamically create a component mapping to the extension property type if it exists:
public abstract class DomainMap<T> : ClassMap<T>, IDomainMap where T : DomainEntity
{
protected DomainMap()
{
// For every DomainEntity class, use the Id property
// as the Primary Key / Object Identifier
Id(x => x.Id).ColumnName(“id”).GeneratedBy.GuidComb();
WithTable(typeof(T).Name);
Map(x => x.LastModified);
Map(x => x.Created);
if (ExtensionProperties.HasExtensionFor(typeof(T)))
{
var componentType = typeof (ExtensionComponent<>)
.MakeGenericType(ExtensionProperties.ExtensionFor(typeof (T)));
var component = Activator.CreateInstance(componentType) as IMappingPart;
AddPart(component);
}
}
}
public class ExtensionComponent<T> : ComponentPart<T>
{
public ExtensionComponent()
: base(ReflectionHelper.GetProperty<DomainEntity>(x => x.ExtendedProperties), false)
{
SetAttribute(“class”, typeof(T).AssemblyQualifiedName);
// Assume that all properties are persistable
typeof(T).GetProperties()
.Where(x => x.DeclaringType == typeof(T))
.Each(prop =>
{
// See the “x_” prefix for the naming convention?
Map(prop, “x_” + prop.Name);
});
}
}
The awesome thing about using an “internal DSL” like that in Fluent NHibernate is that you still have every single bit of the real language at your disposal. The code above effectively extends our NHibernate mapping to persist all the properties of the Extends<T> objects whenever the host entity is persisted. By doing this we largely make the extension property persistence transparent to the main code.
Step 3: Validating the Extension Properties
This part is actually pretty simple, but it doesn’t really translate all that well to other codebases. For validation we use a homegrown mini-framework I built years ago that lives (largely abandoned) in the ShadeTree repository on Google Code (it hasn’t changed a lot since this post on the Notification pattern). This tooling allows you to mix validation between attributes and special methods on a Domain Entity. Going back to our DomainEntity supertype, we make it implement the ShadeTree “IValidated” interface and in the single Validate(Notification) method, we do a transparent pass through to validate the extension properties:
[Serializable]
public class DomainEntity : Entity, IValidated
{
public virtual void Validate(Notification notification)
{
if (ExtendedProperties == null) return;
Validator.ValidateObject(ExtendedProperties, notification);
}
public object ExtendedProperties { get; set; }
}
Not really much to this. Your mechanics will be different for other validation frameworks, but the principle is still the same. Just validate the extension property object if it exists and add those messages to whatever your framework uses for its Notification.
Step 4: Putting this stuff on the screen
Most of our online forms present the main fields in a pretty standard 2 column layout. For the moment, we’re trying to get away with just adding any extension properties to the bottom of the two columns and hope that’s good enough for most clients. Once Chad and I made that assumption I moved onto making a new Html helper that would dynamically add the labels and editable fields for any extension properties:
<%=this.ExtensionFieldsForView()%>
This helper needs to discover the proper extension type, if any, and write a label/field pair for each property it discovers on that extension object:
// Discover what extension type, if any, is valid for the ViewModel of this page
public static HtmlTag ExtensionFieldsForView<TViewModel>(this IDovetailViewWithModel<TViewModel> view) where TViewModel : class
{
var form = buildFormFor(view);
var entityModel = view.Model as EditEntityModel;
return form == null ? HtmlTag.Empty() : form.WriteView(entityModel.Target);
}
private static IExtensionForm buildFormFor<TViewModel>(IDovetailViewWithModel<TViewModel> view) where TViewModel : class
{
var editModel = view.Model as EditEntityModel;
if (editModel == null) return null;
var entityType = editModel.EntityType;
if (!ExtensionProperties.HasExtensionFor(entityType)) return null;
// The Form “magic” happens in ExtensionForm<,>
var extensionType = ExtensionProperties.ExtensionFor(entityType);
var extenderFormType = typeof(ExtensionForm<,>).MakeGenericType(extensionType, entityType);
return (IExtensionForm)view.Container.GetInstance(extenderFormType);
}
One of my goals with the extension properties was to utilize our existing infrastructure for Html generation. We have a large investment in the FubuMVC Html conventions and it makes our form creation quick and consistent. In order to use the Html conventions with our extension properties, I use this utility class to mediate between the dynamically discovered extension properties and the Fubu Html conventions:
public interface IExtensionForm
{
HtmlTag WriteNew();
HtmlTag WriteView(DomainEntity entity);
}
public class ExtensionForm<T, TEntity> : IExtensionForm where T : Extends<TEntity>, new() where TEntity : DomainEntity
{
private readonly TagGenerator<T> _tags;
public ExtensionForm(TagGenerator<T> tags)
{
_tags = tags;
// This is consistent w/ our naming convention
_tags.ElementPrefix = typeof (TEntity).Name;
}
private HtmlTag writeFields(string profileName, IEnumerable<PropertyInfo> properties)
{
_tags.SetProfile(profileName);
return new HtmlTag(“dl”, x =>
{
x.AddClass(“details”);
properties.Each(prop =>
{
x.Add(“dt”).Text(LocalizationManager.GetHeader(prop));
var request = _tags.GetRequest(new FubuMVC.Core.Util.SingleProperty(prop));
HtmlTag inputTag = _tags.InputFor(request);
x.Add(“dd”).Child(inputTag);
});
});
}
public HtmlTag WriteNew()
{
_tags.Model = new T();
return writeFields(TagProfile.DEFAULT, ExtensionFieldRegistry.NewProperties<T>());
}
public HtmlTag WriteView(DomainEntity entity)
{
_tags.Model = (T) entity.ExtendedProperties ?? new T();
return writeFields(DovetailViewActivator.EDIT_PROFILE, ExtensionFieldRegistry.ViewProperties<T>());
}
}
TagGenerator<T> is the main service in FubuMVC.UI for generating HtmlTag’s by convention. As you can probably surmise from the code above, our localization machinery can lookup the header name for a PropertyInfo (or Expression) and that’s how we build the labels. The actual editor tag is completely generated by the FubuMVC machinery based on its policies that we’ve configured:
public class DovetailHtmlConventions : HtmlConventionRegistry
{
public DovetailHtmlConventions()
{
validationAttributes();
numbers();
dates();
Editors.Builder<ListValueDropdownBuilder>();
Editors.IfPropertyIs<bool>().BuildBy(request => new CheckboxTag(request.Value<bool>())
.Style(“width”, “auto !important”)
.Attr(“value”, request.ElementId));
Editors.Always.Modify((request, tag) =>
{
tag.Attr(“label”, request.Header());
tag.Attr(“name”, request.ElementId);
});
}
private void numbers()
{
Editors.IfPropertyIs<Int32>().Attr(“max”, Int32.MaxValue);
Editors.IfPropertyIs<Int16>().Attr(“max”, Int16.MaxValue);
Editors.IfPropertyIs<Int64>().Attr(“max”, Int64.MaxValue);
Editors.IfPropertyTypeIs(t => t.IsIntegerBased()).AddClass(“integer”);
Editors.IfPropertyTypeIs(t => t.IsFloatingPoint()).AddClass(“number”);
}
private void dates()
{
Editors.IfPropertyTypeIs(t => t.IsDateTime()).Modify(x =>
{
if (!x.HasMetaData(EditInPlaceBuilder.EDITABLE_ATTRIBUTE_NAME))
{
x.AddClass(“DatePicker”);
}
});
Editors.If(prop => prop.Accessor.InnerProperty.IsDateAndTime()).Modify(x =>
{
if (!x.HasMetaData(EditInPlaceBuilder.EDITABLE_ATTRIBUTE_NAME))
{
x.AddClass(“time-picker”);
}
});
}
private void validationAttributes()
{
Editors.AddClassForAttribute<RequiredAttribute>(“required”);
Editors.ModifyForAttribute<MaximumStringLengthAttribute>((tag, att) =>
{
if (att.Length < Entity.UnboundedStringLength)
{
tag.Attr(“maxlength”, att.Length);
}
});
Editors.ModifyForAttribute<GreaterOrEqualToZeroAttribute>(tag => tag.Attr(“min”, 0));
Editors.ModifyForAttribute<GreaterThanZeroAttribute>(tag => tag.Attr(“min”, 1));
}
}
And of course, by using the normal Html conventions to build the Html input tags, we get the jQuery validation integration for our extension properties.
Oh, and because Reflection scanning can be expensive, we cache the PropertyInfo’s for each extension type:
public static class ExtensionFieldRegistry
{
private static readonly Cache<Type, IEnumerable<PropertyInfo>> _newProps
= new Cache<Type,IEnumerable<PropertyInfo>>(findNewProps);
private static readonly Cache<Type, IEnumerable<PropertyInfo>> _viewProps
= new Cache<Type,IEnumerable<PropertyInfo>>(findViewProps);
private static IEnumerable<PropertyInfo> findNewProps(Type type)
{
return type.GetProperties().Where(x => x.DeclaringType == type && x.HasAttribute<ShowNewAttribute>());
}
private static IEnumerable<PropertyInfo> findViewProps(Type type)
{
return type.GetProperties().Where(x => x.DeclaringType == type);
}
public static IEnumerable<PropertyInfo> NewProperties<T>()
{
return _newProps[typeof (T)];
}
public static IEnumerable<PropertyInfo> ViewProperties<T>()
{
return _viewProps[typeof (T)];
}
}
Step 5: Getting data from the browser to the server
One more step (we also do edit in place editing of the extension properties in the UI, but that code is a mess and I’m not showing it to anyone until it’s cleaner). We have the html tags on the browser and NHibernate knows how to persist the fields. All we have to do now is get the data from the browser and marshal it into the new extension property type on the server. The ViewModels for submitting the “new” forms all inherit from this base type that, conveniently enough, has a property for the extension properties:
public class UpdateEntityModel<T> : IItemRequest where T : DomainEntity
{
public Extends<T> ExtendedProperties { get; set; }
public Guid Id{ get; set; }
}
I’m omitting some (lots) of code here, but I used an extension point on FubuMVC’s model binding to “fill” that ExtendedProperties property above with this code:
public class ExtensionPropertyBinder : IPropertyBinder
{
// This only applies to properties that close Extends<>
public bool Matches(PropertyInfo property)
{
return property.PropertyType.Closes(typeof (Extends<>));
}
public void Bind(PropertyInfo property, IBindingContext context)
{
var entityType = property.PropertyType.GetGenericArguments()[0];
// If there is no Extends<> for the entity type, do nothing
if (!ExtensionProperties.HasExtensionFor(entityType))
{
return;
}
var extensionType = ExtensionProperties.ExtensionFor(entityType);
// direct the FubuMVC model binding to resolve an object of the
// extensionType using “entityType.Name” as the prefix on the form data,
// and place the newly created object using the specified property
context.BindChild(property, extensionType, entityType.Name);
}
}
Wrapping Up
We’ll see how it goes in the end, but I’m relatively happy with how it all turned out. The end result is that we can just build a very simple class with properties and some attribute declarations, put that assembly in the right spot, and voila!, the right stuff appears on the screen.
Lots of stuff to throw at you, but I’ll try to answer any questions.