Saturday, April 11, 2009

Explicit roles and pipelining strategies

After watching an excellent presentation by Udi Dahan this morning I have rethought some of my infrastructure concerns and the way I can handle certain aspect of my generic stack that I heavily lean on. On example that is relatively low hanging fruit is the persistence mechanism.

As a bit of background: I use a service locator pattern heavily in my code where dependency injection is not appropriate which just keeps things clean, lessening to knowledge of the underlying mechanisms and infrastructure concerns. Currently one good example of where this is used is in my application presentation level code to assist in navigation. We call a basic method

NavigateTo<T>(Action<T> preInit) where T: IPresenter


The service locator gets a presenter of type T and the DI container (which is the same thing as the service locator) instantiates the presenter with its view and any other decencies. Based on the type of the view the presenter has the navigation implementation displays it accordingly. As the application expands we can extend this to be able to do more specific actions, however the coding calling the NavigateTo does not have to know how the views are arranged.



The part that I am most interested in is the very specific example the Udi raised in his talk: Persistence.



I have been involved in a couple of projects that did exactly what Udi old school example did. We had an IEntity interface with a validate method contract on it. Everyone of our classes had to override this and it was messy when it came to validating children for the exact reason Udi mentioned. I think at one stage we even had reflection getting jammed in... it was a mess. Looking back a lot of this could have been cleaned up by implementing the IValidate<IEntity> that Udi proposed. This validator can be incorporated in the concrete persistence mechanism as part of a persistence pipeline.



calling IRepository<IEntity>.Persist(IEntity entity) would under the covers also potentially call a bunch of other infrastructure concerns




=>ILog.Log("IRepository<IEntity>.Persist(IEntity entity)", entity, user)



=>IPersistSecurity<IEntity>.Auth(user)



=>IValidate<IEntity>.Validate(entity)



=>NHibernateSession.Save(entity)



=>IAudit<IEntity>.Audit(entity, user)



=>ILog.Log("IRepository<IEntity>.Persist(IEntity entity)", entity, user)




Each one of these infrastructure concerns can be left generic, allowing a service locator to give you the concrete implementation of the type. eg the IValidateEntity<Customer> may be a customer validator that just calls the validate method on the customer itself... or it may interrogate the customers getters and evaluate based on those values. It may even ask the service locator for an instance of IValidateEntity<Order> and validate each of the orders in the customer that it has been passed. How it is done is not up to customer any more, and it is certainly not up to the persistence mechanism...it is now separated cleanly into its own role.



NB: The fact that when saving an address means that the service locator is calling for an IPersistSecurity<Address> type and that may not exist is great! If there is no defined IPersistSecurity<Address> then we can explicitly say there is a a default return value of "IsValid = true" using a null type (or however you want to implement it). The infrastructure concerns can be pushed aside and dealt with if and when required.



This also raises the question of AOP and policy injection. This pseudo code above implies that we call methods on each of these interfaces. This does not have to be in inline code. This can be added at run time and configured on the fly depending on the application is question.



Now our Entity can focus on what it needs to do and not worry about the myriad of other infrastructure concerns that can be dumped onto it. I am looking forward to this simple modification that should clean things up nicely.

1 comment:

Unknown said...

Should have mentioned that MEF could be a prime candidate here for bolt on extensibility.... will have to scope it out with the release of MEF Preview 5:
http://codebetter.com/blogs/glenn.block/archive/2009/04/11/mef-preview-5-changes-and-enhancements.aspx