Watch your Composition Root

Watch your Composition Root

IoC is a great pattern that can help make business logic easier to maintain and independently test, all while decreasing complexity. However, in large applications object dependency graphs can become quite complicated making it very easy to lose control. So how can we mitigate this? Well, split up your registration responsibilities of course.

The code in this post is compatible with .NET standard 2.0.

This post is an example of how I attempt to control the definition of complicated object graphs. I will be using SimpleInjector in my example below but the same concept could be used for any other framework. With an interface, a static class, and a touch of reflection we can bootstrap like a boss.

The interface:

public interface IBootstrap
{
    void Bootstrap(Container container);
}

The Static class:

public static class Bootstrapper
{
    public static Container Container;

    static Bootstrapper()
    {
        Container = new Container();

        foreach (var bootstrapType in GetBootstrapTypes())
        {
            var bootstrap = (IBootstrap)Activator.CreateInstance(bootstrapType);
            bootstrap.Bootstrap(Container);
        }

#if DEBUG
        Container.Verify(VerificationOption.VerifyAndDiagnose);
#else
        Container.Verify();
#endif
    }

    private static IEnumerable<Type> GetBootstrapTypes() =>
        from assembly in AppDomain.CurrentDomain.GetAssemblies()
        from type in assembly.GetExportedTypes()
        where typeof(IBootstrap).IsAssignableFrom(type) && !type.IsAbstract
        select type;
}

This example takes advantage of the fact that static constructors are "called automatically to initialize the class before the first instance is created or any static members are referenced"[1]. Let's look at what is going on:

  1. Creates a new container.
  2. Finds all concrete types that implement IBootstrap in the current application domain.
  3. Iterates over all of these types:
    • Creates an instance.
    • Calls the Bootstrap method passing it the container.
  4. Verifies the container.

With this code, you can easily split up your composition root into different classes, files and folders, each one responsible for the registration of dependencies in a focused area.

This approach also works well in conjunction with a plugin pattern, as it allows plugins to register their own dependencies and decorators. To dynamically discover and register plugins is straightforward when using SimpleInjector, so I will be using the example from its documentation.

var pluginDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");

var pluginAssemblies =
    from file in new DirectoryInfo(pluginDirectory).GetFiles()
    where file.Extension.ToLower() == ".dll"
    select Assembly.LoadFile(file.FullName);

Container.RegisterCollection<IPlugin>(pluginAssemblies);

This code will live just before the iteration of classes that implement IBootstrap. By adding the code here we are able to load the plugin assemblies into the AppDomain and register the plugins before theIBootstrap type discovery so any extra dependencies the plugin requires can be included.

Here is a sample application that brings these concepts together.

What do you think? Please leave a comment letting me know your opinion or if there is a gaping issue with this approach. I am always keen to know how and what others are doing. One of the main aims of me creating this blog was to try and become a better programmer by putting my thoughts into words, but there is no reason that it can't be a two-way street.


  1. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors ↩︎