Design patterns and practices in .NET: the Service Locator anti-pattern

Introduction

The main responsibility of a Service Locator is to serve instances of services when consumers request them. The pattern is strongly linked to Dependency Injection and was introduced by Martin Fowler here.

The most common implementation of the pattern introduces a static factory. This factory can be configured with concrete services in the composition root of the application, such as global.asax, Main, etc., depending on the type of the application you’re developing. In other words the configuration happens before the first consumer can use it to extract a concrete service. Here you can think of a service as roughly equal to a dependency: the CustomerController has a dependency on ICustomerService. CustomerService has a dependency on ICustromerRepository etc. So when a concrete implementation of the abstraction is needed then the caller tries to grab it from the Service Locator.

A Service Locator is quite similar to Inversion-of-Control (IoC) containers at first. If you’re familiar with some IoCs such as StructureMap or CastleWindsor, then you’ll know that you can register your concrete types in the composition root. In StructureMap you can do this explicitly as follows:

x.For<ICustomerService>().Use<ConcreteCustomerService>();

The Service Locator configuration starts off in a similar manner. It’s essentially a dictionary of abstractions and their desired concrete types: ICustomerRepository – CustomerRepository; IProductService – ProductService. This is perfectly legitimate to do from the composition root. As we will see later consulting the service locator elsewhere in the application for concrete services is an anti-pattern.

Demo

We’ll simulate a dependency between the CustomerService and CustomerRepository classes where CustomerService requires a customer repository to consult the database for queries on the customer domain. Open Visual Studio and add the following standard generic implementation of a ServiceLocator:

public static class ServiceLocator
{
	private readonly static Dictionary<Type, object> _configuredServices = new Dictionary<Type, object>();

	public static T GetConfiguredService<T>()
	{
		return (T)ServiceLocator._configuredServices[typeof(T)];
	}

	public static void Register<T>(T service)
	{
		ServiceLocator._configuredServices[typeof(T)] = service;
	}
}

This is a very minimalistic implementation of the Service Locator. It’s void of exception handling, guard clauses, loading the dependency graph from an XML file but those features only add noise to the main discussion. The dependency map is stored in the private dictionary and the Register method is used, as you’ve probably guessed it, to register dependencies. It is analogous to the .For and .Use extension methods in the StructureMap example above. Let’s add the following interfaces and classes to see how the locator can be used:

public class Customer
{
}
public interface ICustomerService
{
	Customer GetCustomer(int id);
}
public interface ICustomerRepository
{
	Customer GetCustomerFromDatabase(int id);
}
public class CustomerRepository : ICustomerRepository
{
	public Customer GetCustomerFromDatabase(int id)
	{
		return new Customer();
	}
}
public class CustomerService : ICustomerService
{
	private ICustomerRepository _customerRepository;

	public CustomerService()
	{
		_customerRepository = ServiceLocator.GetConfiguredService<ICustomerRepository>();
	}

	public Customer GetCustomer(int id)
	{
		return _customerRepository.GetCustomerFromDatabase(id);
	}
}

You should be able to follow along this far. The CustomerService class resolves its own dependency using the ServiceLocator. You can configure the locator in Main as follows:

static void Main(string[] args)
{
	ServiceLocator.Register<ICustomerRepository>(new CustomerRepository());
	Customer c = new CustomerService().GetCustomer(54);
}

Main represents the composition root of a Console application so that’s where you can register the dependency graph. Step through the app with F11 and you’ll see that CustomerRepository is registered and retrieved as expected.

The CustomerService class can resolve its own dependency on ICustomerRepository, so what’s the problem? We can register our concrete implementations, retrieve the stored implementation where it’s needed, register Mock objects as concrete types in a Test Driven Design scenario, program against abstractions, write maintainable code, support late binding by changing the registration, so you’re a happy bunny, right? You shouldn’t be as the ServiceLocator class has a negative effect on the re-usability of the classes that consume it:

  • The ServiceLocator dependency will drag along if you try to re-use a class with a call to the locator
  • It is not obvious for external clients calling CustomerService() that Dependency Injection is used

The CustomerService will loosely depend on CustomerRepository through the ICustomerRepository interface. This is perfectly legitimate and valid. However, it will be tightly coupled to the ServiceLocator class. Here’s the dependency graph:

Dependency graph with service locator

If you want to distribute the CustomerService class then you’ll have to attach the ServiceLocator class to the package. It must come along even if the person that wants to use your class is not intending to use the ServiceLocator class in any way because they have their own Dependency Injection solution, such as StructureMap or CastleWindsor. Also, the consumer will need to set up ServiceLocator in the composition root otherwise they will get an exception. As the ServiceLocator may well reside in a different module, even that module must be redistributed for the CustomerService to be usable.

ProductService forces its users to follow the Dependency Injection strategy employed within it. There’s no room for other strategies unfortunately. Developers must simply accept the existence of the service locator. Also, there’s no way of telling that there’s a direct dependency just by looking at its signature which is what consumers will see first when creating a new CustomerService object. If the consumer doesn’t set up ServiceLocator appropriately then they will get an exception when using the CustomerService constructor. Depending on the exception handling strategy all they may get is a KeyNotFoundException. The consumer will then ask the questions: what key? Why is it not found? What are you talking about? WHY YOU NO WORK!!??? The consumer must know about the internals of the ConsumerService class which usually indicates a higher-than-desired level of coupling.

We can therefore rule out this patterns as it brings with it a fully redundant dependency which we can get rid of easily using constructor injection:

public CustomerService(ICustomerRepository customerRepository)
{
	_customerRepository = customerRepository;
}

There’s simply no advantage with this pattern that cannot be solved with an alternative solution such as constructor injection coupled with an IoC container. ProductService as it stands is not self-documenting. Its signature does not reveal anything about its dependency needs. Imagine that you download this API from NuGet and call CustomerService service = new CustomerService(). Your assumption would be that this is a fairly simple class that does not have any external dependencies which is not true as it turns out.

You can misuse IoC containers in the same way actually. It’s fine to use IoCs to resolve your dependencies “behind the scenes” but they – or at least some of them – allow the users to fetch concrete types from the container. In StructureMap you’d do it as follows:

StructureMap.ObjectFactory.Container.GetInstance<CustomerRepository>()

You should avoid using this type of dependency resolution for the same reasons why you wouldn’t use a ServiceLocator and its GetConfiguredService method.

Note that this pattern being an anti-pattern is a controversial topic. You can check out this post that offers another viewpoint and argues that Service Locator is indeed a proper design pattern.

View the list of posts on Architecture and Patterns here.

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: