A new requirement has come in to our system, essentially we need to be able to tell external systems when (some) changes happen to our domain objects. How we notify the systems is one issue but how we actually track domain changes is another one.
Some of the changes are quite high level, such as a Customer state change, and some are just little changes to the data of an object, such as a change to a Customers first name.
We could handle the high level state changes by calling through Service classes, for example CustomerStateChangeService.Activate(customer). However we also need to think about how to track all the data changes, we've considered a few ways that we could do this and two of the most obvious are:
- Services - Instead of directly setting a Customers first name call through a Service which would then take care of ensuring that we give other systems a chance to track the change. We could look at something like the anticorruption layer example.
- Domain Events - The domain classes could raise events, to some extent this is nicely decoupled but it bothers us that our domain objects would be raising events simply because an external system needs to know about the change. It also means that pretty much every property setter in the domain could potentially be raising an event once its set the value, not very elegant.
- NHibernate - Maybe include an interceptor that can help us work out what has changed when NHibernate comes to persist. This sounds easy but in practice would be a mess and certainly isn't very intuitive.
Anyway these and other solutions didn't appeal so I started looking at PostSharp. Now I'm no fan of overly technical solutions to domain problems. However I'd heard good things about AOP and thought it might help us solve the problem, especially since logging is quite often given as an example of where AOP can help. I was thus hoping that it could give us a good, simple, solution to our problem.
However when I came to use PostSharp I couldn't believe how good it was. In particular it allows you to use attributes to specify code that you want PostSharp to inject in post-build...
To show how simple it is I've tried to come up with what I consider to be a useful example which you can get from google code. In order to run it you first need to install PostSharp, once you've done this you can open and run the project.
First off note that SimpleDomainClass is marked with AllPropertySetsNotificationAttribute. If you open AllPropertySetsNotificationAttribute you can see that it is using reflection to attach an extra attribute, PropertySetNotificationAttribute, to each property setters.
This may seem silly until you run the application and see that the code in PropertySetNotificationAttribute.OnSuccess is being automagically run.
This is happening because the post-build PostSharp process is ensuring that where it sees a method boundary aspect, such as PropertySetNotificationAttribute, it inserts the necessary IL to ensure the code in the attribute is run.
You can see this if you open Reflector and view the disassembly output for SimpleDomainClass, look at the property setters and note the extra code including the call to OnSuccess in the attribute.
In case you were wondering this is a simplified version of the binding sample that actually comes with PostSharp.
In our case you could easily imagine PropertySetNotificationAttribute calling out to a IDomainChangeNotificationService, the actual implementation could be retrieved from a Service Locator or injected in using IOC.
You will note that our domain assembly references the assembly that I've put the attributes into and also PostSharp.Laos and PostSharp.Public. This bothers me slightly but from what I can see they are quite lightweight assemblies so it doesn't seem to be a killer.
I'm still at the earlier stages of understanding PostSharp but I'm very impressed.
Like TypeMock and NHibernate it is very powerful and also seems to come with very good documentation.
I'm also stunned by how easy it was to do what I wanted to in this case, but then I guess I shouldnt be as the docs indicate that Laos has been designed to be simple rather than being fully featured.