Throw Out Your Dependency Injection Container

Dependency Injection

Dependency injection containers (aka inversion of control containers) seem to be common in most modern applications.  There is no argument against the value dependency injection, but I do have a couple arguments against using a dependency injection container.  Like many other design patterns and practices, over time the development community seems to forget the original problem the pattern or practice was solving.

Constructor & Setter Injection

Passing your dependencies via the constructor is generally a better way of injecting dependencies instead of  setters. Constructor injection gives you a much clearer definition of what dependencies are needed in order to construct the object into a valid state.  My dependencies fields are usually readonly, to prevent them from being externally set after object creation.

Regardless if you use constructor or setter injection with your container of choice, there is often one injection method that is seemingly rarely used or mentioned.  Pass your dependency to the method that requires it.

How often have you seen a constructor take a pile of dependencies via it’s constructor, to which those dependencies were used in a limited amount of methods.  I often see this when looking at CQRS Command Handlers.

class InventoryHandler
{
	private readonly IRepository _repository;
	private readonly IBus _bus;
	private readonly IValidateInventory _validation;
	
	public InventoryHandler(IRepository repository, IBus bus, IValidateInventory validation)
	{
		_repository = repository;
		_bus = bus
		_validation = validation;
	}

	public void Handle(ReceiveInventory cmd)
	{
		if (!_validation.IsAvailable(cmd.ProductId)) {
			throw new InvalidOperationException("Cannot receive inventory");
		}

		// Receive Product into Inventory
		var inventory = _repository.GetById(cmd.ProductId);
		inventory.Receive(cmd.Quantity, cmd.UnitCost);
	}

	public Handle(RemoveInventory cmd)
	{
		// Remove product from inventory
		var inventory = _repository.GetById(cmd.ProductId);
		inventory.RemoveInventory(cmd.Quantity);

		// Pub/Sub to notify other bounded context of inventory removal
		_bus.Send(new InventoryRemovedMessage(cmd.InventoryId));
	}
}

In this simplified example, IRepository is used in both Handle() methods. However, IBus & IValidateInventory are both only used in one method each.

Instead of passing the dependencies via constructor, pass the required dependencies to the method that requires it.

class InventoryHandler
{
	private readonly IRepository _repository;
	
	public InventoryHandler(IRepository repository)
	{
		_repository = repository;
	}

	public void Handle(IValidateInventory validation, ReceiveInventory cmd)
	{
		if (!validation.IsAvailable(cmd.ProductId)) {
			throw new InvalidOperationException("Cannot receive inventory");
		}

		// Receive Product into Inventory
		var inventory = _repository.GetById(cmd.ProductId);
		inventory.Receive(cmd.Quantity, cmd.UnitCost);
	}

	public Handle(IBus bus, RemoveInventory cmd)
	{
		// Remove product from inventory
		var inventory = _repository.GetById(cmd.ProductId);
		inventory.RemoveInventory(cmd.Quantity);

		// Pub/Sub to notify other bounded context of inventory removal
		bus.Send(new InventoryRemovedMessage(cmd.InventoryId));
	}
}

 

Nested Dependencies

The primary reason why I prefer not to use a dependency injection container is because it potentially masks a code smell.  Nested dependencies or over abstraction can add complexity by adding unnecessary layers.

Have you seen something similar to this before?

public class MyService(IRepository repostiory) { ... }
public class Repository(IUnitOfWork unitOfWork) { ... }
public class UnitOfWork(IDbContext dbContext) { ... }
public class DbContext(IDbConnection dbConnection) { ... }
public class DbConnection(string connectionString) { ... }

Code SmellIf you are using a dependency injection container, you may not have noticed the excessive and unneeded abstraction layers in parts of you application.  If you had to manually wire up up these nested dependencies through all the layers, it would be really painful.

Masking the pain by using a dependency injection container is making your application more complex.

Take a look at your application and see if you can remove your dependency injection container.   If you can, then what benefits did it provide?  If you can’t, ask yourself if you really need all those layers.

  • Martin Anderson

    Would you mind explaining how “pass your dependency to the method that requires it” is different from the visitor pattern? It’s a little strange that you didn’t mention it even in passing.

  • Alex Gill

    I have a number of objections to this post.

    1. Method injection in this way violates encapsulation by tightly coupling the implementation of the method to its signature. As Mark Seeman explains in his blog – http://blog.ploeh.dk/2011/02/28/Interfacesareaccessmodifiers/ (very much worth reading if you’re interested in DI – he literally wrote the book), constructors are implementation details. By moving a dependency from a constructor to a public method, you make the dependency part of the contract.

    2. If your consuming class is required to pass the validation class and the bus class, where does the consumer get those from? Are these dependencies passed all the way through your application via methods? Code quickly becomes unreadable if every method call has to take an extra N arguments. In Clean Code, Robert C. Martin recommends that functions should have as few arguments as possible – see http://stackoverflow.com/a/175035/1860652

    3. You are no longer able to implement a common generic interface across all command handlers, which is crucial for simple decorator-based AOP. See DotNetJunkie’s blog – https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91 – incidentally the creator of the best IoC container, Simple Injector

    4. The example given does not follow the Single Responsibility Principle. Why does InventoryHandler have two methods, one for ReceiveInventory and one for RemoveInventory? Instead of pushing dependencies into the API, split this class into two and your problem vanishes. If you want to have both methods available on a single class, employ the Facade pattern: http://blog.ploeh.dk/2010/02/02/RefactoringtoAggregateServices/

    • Thanks for the feedback and comment.

      I generally do only implement one handler per command and use the decorator pattern for adding validation. I think this post deserves an update on how to re-implement the example above.

      Again, really appreciate the feedback!