Tuesday, October 7, 2008

Register services in Windsor - take 2

I had to write my Find service method in almost every solution I made so it was time to add it to Castle. Now you can write this to auto register all your services, repositories, views, etc:

[TestFixture]
public class AllTypesTestCase
{
[Test]
public void Should_register_by_service_interface()
{
var kernel = new DefaultKernel();
kernel.Register(AllTypes
.Of<IService>()
.FromAssembly(typeof(ProductService).Assembly)
.WithService.FromInterface());

Assert.AreEqual(typeof(ProductService), kernel.Resolve<IProductService>().GetType());
Assert.AreEqual(typeof(OrderService), kernel.Resolve<IOrderService>().GetType());
}
}

public interface IService
{
}

public interface IProductService : IService
{
}

public class ProductService : IProductService
{
}

public interface IOrderService : IService
{
}

public class OrderService : IOrderService
{
}

Thanks Craig Neuwirt for applying my patch (with some modifications) to Castle.

Friday, October 3, 2008

Keeping Quality

I got a comment some days ago that the design and quality decreases over the lifetime of the product and that it's not possible to keep the initial quality. I strongly disagree*. This is where TDD and NDepend comes to a rescue. TDD is not (only) about test but also design. What is the first thing you write in your test method? Is it an Assert statement or is it how you interact with your service under test? If you skip TDD when the product becomes bigger then your quality will decrease.

But how do you set design rules for your system e.g. that you should follow DIP? That's where NDepend comes to rescue. Set up a rule that says "do not depend on a specific external system". If you have your infrastructure, external dependencies, etc in their own assemblies (App.Repository for example) then you can write the rule something like this:

  WARN IF Count > 0 IN
SELECT NAMESPACES FROM ASSEMBLIES "App.Model"
WHERE IsDirectlyUsing "App.Repository.*"
If someone adds a reference to this assembly from your model, tomorrow, next summer or whenever, then the alarm will start to sound!

If you do some clever rules and use TDD then I think you can have design and code quality in the future as well. But, and it's a big but, you can't and you shouldn't put all the trust to the tools. You need co-workers that understand why you should follow the dependency inversion principle, how to write good test, and so on. That's the greatest chance to keeping good quality in your system!

* I disagree that it's not possible, not that it's a fact for most systems.

Thursday, October 2, 2008

Register services in Windsor

This code handles almost all your future Windsor components. Very nice in my opinion!

protected virtual void InitializeWindsor()
{
if (container == null)
{
container = new WindsorContainer();

ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(Container));

//register all services
container.Register(
AllTypes.FromAssembly(typeof (ProductService).Assembly)
.BasedOn<IService>()
.WithService.Select(type => FindService(type, typeof (IService))));

//register all repositories
container.Register(
AllTypes.FromAssembly(typeof (ProductRepository).Assembly)
.BasedOn<IRepository>()
.WithService.Select(type => FindService(type, typeof (IRepository))));

//and so on...
}
}

private static Type FindService(Type type, Type implements)
{
return FindService(type, implements, new Type[0]);
}

private static Type FindService(Type type, Type implements, ICollection<Type> typesToIgnore)
{
Type first = null;
Type[] interfaces = type.GetInterfaces();
foreach (Type theInterface in interfaces)
{
if (theInterface.GetInterface(implements.FullName) != null && !typesToIgnore.Contains(theInterface))
{
first = theInterface;
break;
}
}
return first;
}

The alternative is this. But this one doesn't handle future components and you have to register them one by one.

protected virtual void InitializeWindsor()
{
if (container == null)
{
container = new WindsorContainer();
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(Container));

// Register all services
container.Register(
Component
.For<IProductService>()
.ImplementedBy<ProductService>());

container.Register(
Component
.For<IOrderService>()
.ImplementedBy<OrderService>());

// continue with all other services, repositories, etc..
}
}

What is the cost of a story

When estimating the cost of finishing a story most people only thinks about implementation. What's really important is the cost to get it to production and maintenance. Managers stress a low cost on implementation but that can lead to a high maintenance cost. If you think about how much it cost to finish a project compared to maintaining the product, then hopefully you realize that having a product and a team that can handle changes is very important. Some people think that after they get the system into production then the workload will go down quite a lot. But the opposite can happen because then you have both production issues and new change request/defects to handle. That's why it's so important to have a good way to handle changes because they will come. I don't think that your product will be version 1.0 that long.

There are many ways to handle changes from software development to organization. I will talk about some of them in future posts. Stay tuned.