Wednesday, May 26, 2010

Service Coordinator thing

So working on some new code we got the first chunk of functionality up and running yesterday but there was something in particular that bugged me.

Currently the code is organized into a Domain project which basically just has a couple of infrastructure/configuration classes and a service class (which are suppose to kind of handle a domain action for the consumer and not just a bunch of utility things). Then there is a Domain.Models that actually has our object model. Finally out of these core projects of importance there is a Domain.DAL that has all the NHibernate mapping code and our repositories.

The service class is the one that actually gets used and it turns around and uses the models and repositories to get the job done. Now to get some stuff working I ended up having to write this code:

try
{
  // ... check for and get the external input ...
  using (var unit = new UnitOfWork())
  {
    var service = new ServiceClass();
    service.Process(inputs);
    unit.Commit();
  }
}
catch (ApplicationException e)
{
  var logger = LoggerFactory.GetLogger(this.GetType());
  logger.Error(e);
  // ... any extra context based error logging ...
}

This seems like too much to ask of everyone who ever consumes a service. I can only assume that the number of services will grow along with the number of places consuming into the services. I have only written this code once and I didn't like where I put it.

The logger is there because I don't want my service classes to be cluttered with exception logging or unit of work management. I would like them to only worry about their own info/debug logging and be able to use each other and not worry about unit of works in progress and the like. So that pushes more responsibilities to the top.

After some thought I came up with this:

ServiceCoordinator.Run(
  s => s.ProcessMessage(deviceMessage)
);

Basically the ServiceCoordinator is a static class (I'm not normally a fan of static so this might change) that internally will take advantage of a IoC container and actually manage the process of resolving the service and wrapping it up in our generic unit of work and logging stuff. It has currently two functions on it:

void Run<TService>(Action<TService> expression)
TResult Execute<TService, TResult>(Func<TService, TResult> expression)

Internally those functions are implemented basically the same except one explicitly returns a value.

public static TResult Execute<TService, TResult>(Func<TService, TResult>
{
  try
  {
    var service = kernel.Get();
    using (var unit = new UnitOfWork())
    {
      var result = expression.Invoke(service);
      unit.Commit();
      return result;
    }
  }
  catch (Exception e)
  {
    var logger = LoggerFactory.GetLogger(typeof(TService));
    logger.Error(e);
    throw;
  }
}

Initial test and look at it and I'm liking this better. I wasn't originally using dependency injection although I'm familiar with it. The lack of DI was of course resulting in some hard to test scenarios so I think I may turn around and at least completely separate the logical layers using interfaces and DI. Get that hard dependency on UnitOfWork out of the ServiceCoordinator as well.

No comments:

Post a Comment