From Dependency Injection in .NET, Second Edition by Steven van Deursen and Mark Seemann

This article describes where and how programmers should compose an application’s object graphs and the concept of the Composition Root.


Save 37% on Dependency Injection in .NET, Second Edition. Just enter code fccseemann into the discount code box at checkout at manning.com.


Where should we compose object graphs?

As close as possible to the application’s entry point.

When composing an application from many loosely coupled classes, the composition should take place as close to the application’s entry point as possible. The Main method is the entry point for most application types. The Composition Root composes the object graph, which subsequently performs the actual work of the application.

What is a Composition Root?

Definition

A Composition Root is a (preferably) unique location in an application where modules are composed together.


Figure 1. Close to the application is entry point, the Composition Root takes care of composing object graphs of loosely coupled classes. The Composition Root takes a direct dependency on all modules in the system.


Most classes use Constructor Injection. By doing this they push the responsibility of the creation of their dependencies up to their consumer. That consumer -again- push the responsibility of the creation of its dependencies up as well.

We can’t delay the creation of our classes indefinitely. There must be a location where we create our object graphs. You should concentrate this creation into a single area of your application. This place is called the Composition Root.

Listing 1. An example object graph- All components from all different layers in the application are constructed in the Composition Root.

 new HomeController(                                     
     new ProductService(                                 
         new SqlProductRepository(                       
             new CommerceContext(connectionString)),     
         new AspNetUserContextAdapter()));               

❶   User Interface component

❷   Domain Logic component

❸   Data Access components

❹   User Interface component

Warning

In case you use a DI Container, the Composition Root should be the only place where you use the DI Container. Using a DI Container outside the Composition Root leads to the Service Locator anti-pattern, which is discussed in the next chapter.

If we had a Console application that was specialized in operating on this particular object graph, this might look like the following listing.

Listing2. The application’s object graph as part of a Console Application.

 public static class Program
 {
     public static void Main(string[] args)              
     {
         string connectionString = args[0];              
  
         HomeController controller =
             CreateController(connectionString);         
  
         var result = controller.Index();                
  
         var vm = (FeaturedProductsViewModel)result.Model;
  
         Console.WriteLine("Featured products:");        
                                                         
         foreach (var product in vm.Products)            
         {                                               
             Console.WriteLine(product.SummaryText);     
         }
     }
  
     private static HomeController CreateController(     
         string connectionString)
     {
         return
             new HomeController(                         
                 new ProductService(
                     new SqlProductRepository(
                         new CommerceContext(connectionString)),
                     new UserContextStub()));            
     }
 }

❶   The application’s entry point.

❷   Extract a connection string from the supplied command line arguments.

❸   Request the application’s Composition Root to build a new controller instance.

❹   Use the returned controller.

❺   Display the results.

❻   This method acts as the application’s Composition Root.

❼   Build our application’s object graph.

❽   IUserContext implementation that allows the ProductService to function and calculate the discounts.

In this example the Composition Root’s separated from the Main method. This isn’t required—the Composition Root isn’t a method or a class, it’s a concept. It can be part of the Main method, or can span multiple classes.

Note

The Composition Root can be spread out across multiple classes as long as they all reside in a single module.

How Composition Root works

When you write loosely-coupled code, you create many classes that you must compose to create an application. It can be tempting to compose these classes at many different locations in order to create small subsystems, but that limits your ability to Intercept those systems to modify their behavior. Instead, you should compose classes in one single area of your application.

When you look at Constructor Injection in isolation you may wonder, doesn’t it defer the decision about selecting a dependency to another place? Yes, it does, and this is a good thing—this means that you get a central place where you can connect collaborating classes. The Composition Root acts as a third party that connects consumers with their services.

The longer you defer the decision on how to connect classes together, the more you keep your options open. The Composition Root should be placed as close to the application’s entry point as possible.

Even a modular application that uses loose coupling and late binding to compose itself has a root that contains the entry point into the application. Examples are:

  • A Console application is an executable (.exe) with a Program class with a Main method.
  • An ASP.NET Core web application is a library (.dll), It contains Main method as well.
  • A UWP application is an executable (.exe) with an App.xaml file.
  • A WCF service is a library (.dll) with a class that derives from a service interface, although you can hook into a more low-level entry point by creating a custom ServiceHostFactory.

Many other technologies exist, but common to them all is one module contains the entry point of the application: this is the root of the application. The Composition Root of the application should be located in the application’s root to properly compose the application.

Don’t be misled to think that the Composition Rootis part of your User Interface Layer. Even if you place the Composition Root in the same assembly as your User Interface Layer, as we’ll do in the next example, the Composition Root isn’t part of that layer. Assemblies are a deployment artifact—you split code into multiple assemblies to allow code to be deployed separately. An architectural layer, on the other hand, is a logical artifact—you can group multiple logical artifacts in a single deployment artifact. Even though the assembly that both holds the Composition Rootand the User Interface Layer depends on all other modules in the system, the User Interface Layer doesn’t.

Important

The Composition Root is not part of the User Interface layer, even though they might be placed in the same assembly.

It’s not required for the Composition Root to be placed in the same project as your User Interface Layer. You can move the User Interface Layer out of the application’s root project. Advantage of this is that you can prevent the project that holds the User Interface Layer from taking a dependency on for instance the Data Access Layer project. This makes it impossible for UI classes to depend on Data Access classes.

The downside of this approach is that it isn’t always easy to do it. With ASP.NET Core MVC, for instance, although it’s trivial to move Controllers and View Models to a separate project, it can be quite challenging to do the same with your views and client resources.

Separating the presentation technology from the Composition Root might not be that beneficial at all, because a Composition Root is specific to that specific application. Composition Roots are typically not reused.

You shouldn’t attempt to compose classes in any of the other modules because that approach limits your options. All classes in application modules should use Constructor Injection and leave it up to the Composition Root to compose the application’s object graph. Any DI Container in use should be limited to the Composition Root.

Note

Moving composition of classes out of the Composition Root leads to either the Control Freak or Service Locator anti-patterns, which are discussed in the next chapter.

 

Warning

The Composition Root should be the sole place in the entire application that knows about the structure of the constructed object graphs. Application code not only relinquishes control over its Dependencies, it also relinquishes knowledge about its Dependencies. Centralizing this knowledge simplifies development. This does mean though that application code can’t pass on Dependencies to other threads that run parallel to the current operation, since a consumer has no way of knowing whether or not it is safe to do so. Instead, when spinning off concurrent operations, it is the Composition Root’s job to create a new object graph for each concurrent operation.

The Composition Root of listing 2 showed an example of Pure DI. The Composition Root pattern’s both applicable to Pure DI and DI Containers. In the next section we’ll describe how a DI Container can be used in a Composition Root.

Using a DI Container in a Composition Root

A DI Containeris a software library that can automate many of the tasks involved in composing objects and managing their lifetimes.

A DI Container can be misused as a Service Locator, but it should only be used as an engine that composes object graphs. When you consider a DI Container in that perspective, it only makes sense to constrain it to the Composition Root. This also has the great benefit of removing any coupling between the DI Container and the rest of the application’s code base.

Only the Composition Root should have a reference to the DI Container. The rest of the application has no reference to the container and instead relies on the patterns described in this article. DI Containers understand those patterns and use them to compose the application’s object graph.

Important

DI Container should only be referenced from the Composition Root. All other modules should have no reference to the container.

A Composition Root can be implemented with a DI Container. This means that you use the container to compose the entire application’s object graph in a single call to its Resolve method. Whenever we talk to developers about doing it like this, we can always tell that it makes them uncomfortable because they’re afraid that it’s terribly inefficient and bad for performance. You don’t have to worry about that because it’s almost never the case, and in the few situations where this is the case, there are ways to address the issue.

Tip

Don’t worry about the performance overhead of using a DI Container to compose large object graphs. It’s usually not an issue.

When it comes to request-based applications, such as websites and services, you configure the container only once, but resolve an object graph for each incoming request.

Example: Implementing a Composition Root using Pure DI

Imagine a sample e-commerce web application that must have a Composition Root to compose object graphs for incoming HTTP requests. As with all other ASP.NET Core web applications, the entry point is in the Main method. By default, the Main method of an ASP.NET Core application delegates most of the work to the Startup class. This Startup class is close enough to the application’s entry point for us, and we’ll use that as our Composition Root.

As in the previous example with the Console application, we use Pure DI. This means we compose our object graphs using plain old C# code instead of using a DI Container:

Listing 3. The commerce application’s Startup class.

 
 public class Startup
 {
     public IConfigurationRoot Configuration { get; }
  
     public Startup(IHostingEnvironment env)             
     {
         this.Configuration = new ConfigurationBuilder() 
             .SetBasePath(env.ContentRootPath)           
             ...                                         
             .Build();                                   
     }
  
     public void ConfigureServices(                      
         IServiceCollection services)
     {
         services.AddMvc();
  
         var accessor = new HttpContextAccessor();       
  
         services.AddSingleton<IHttpContextAccessor>(accessor);
  
         var connectionString =                          
             this.Configuration.GetConnectionString(
                 "CommerceConnection");
  
         services.AddSingleton<IControllerActivator>(    
             new CommerceControllerActivator(accessor, connectionString));
     }
  
     ...
 }
 

❶   Constructor called by ASP.NET Core upon application startup.

❷   Configuration files are specified and loaded.

❸   Method that gets called by ASP.NET by convention. The supplied IServiceCollection instance allows us to influence the default services that ASP.NET knows about.

❹   Adds a service to the framework that allows retrieving the current HttpContext. Note to MEAP readers: this will be redundant in a future version of ASP.NET Core and we will update this example accordingly before print.

❺   Loads the application’s database connection string from the configuration file.

❻   Replaces the default IControllerActivator with one that builds our object graphs.

The Startup class is a necessity where you apply the required plumbing. Don’t worry if you’re not familiar with ASP.NET Core. The interesting part is our CommerceControllerActivator. The entire setup for the application is encapsulated in the CommerceControllerActivator class, which we’ll show shortly.

To enable to wire up MVC Controllers in the application, you must employ the appropriate Seam in ASP.NET Core MVC, called an IControllerActivator (which is outside the scope of this article). For now, it’s enough to understand that to integrate with ASP.NET Core MVC, you must create an Adapterfor your Composition Root and tell the framework about it.

Note

Any well-designed framework provides us with the appropriate Seams to intercept the creation of framework types. These Seams are most often shaped as factory abstractions, just as MVC’s IControllerActivator.

Because the Startup.ConfigureServices method only runs once, your CommerceControllerActivator class is a single instance which is only initialized a single time.

Because you set up ASP.NET Core MVC with the custom IControllerActivator, MVC invokes its Create method to create a new Controller instance for each incoming HTTP request.

Listing 4. Commerce application’s IControllerActivator implementation.

 
 public class CommerceControllerActivator : IControllerActivator
 {
     private readonly string connectionString;
  
     public CommerceControllerActivator(string connectionString)
     {
         this.connectionString = connectionString;
     }
  
     public object Create(ControllerContext ctx)         
     {
         Type type = ctx.ActionDescriptor.ControllerTypeInfo.AsType();
  
         if (type == typeof(HomeController))             
         {
             return
                 new HomeController(
                     new ProductService(
                         new SqlProductRepository(
                             new CommerceContext(this.connectionString)),
                         new AspNetUserContextAdapter()));
         }
         else
         {
             throw new Exception("Unknown controller."); 
         }
     }
 }
 

❶   This method’s called by ASP.NET Core MVC for every request.

❷   In case MVC asks for a HomeController, the appropriate object graph is built.

❸   The e-commerce application currently only has one controller. Each new controller that you add will have its own if block.

Notice how the creation of the HomeController in this example is almost identical to the application’s object graph that we’ve shown in listing 1.When MVC calls Create, we determine the Controller type, create the correct object graph based on this type.

The Composition Root in this example is spread out across two classes, as shown in figure 2.


Figure 2. The Composition Root is spread across two classes, but they’re all defined within the same module.


The most important thing to notice here is that these two classes are the only classes in the entire sample application that compose object graphs. All the rest of the application code only uses the Constructor Injection pattern.

The apparent dependency explosion

An often heard complaint from developers is that the Composition Root causes the application’s entry point to take a dependency on all other assemblies in the application. In their old tightly-coupled code bases, their entry point only needed to depend on the direct layer below. This seems backward, because DI is meant to lower the required number of dependencies each project has. They see the use of DI to cause an explosion of dependencies in their application’s entry point—or, it may seem.

To get a good view of what it is some developers are worried about, let’s take a look at the dependency graph of Mary’s tightly coupled applicationand compare that with the dependency graph of the loosely coupled equivalent. This complaint comes from the fact that developers misunderstand how project dependencies work.


Figure 3. Comparing the dependency graph of Mary’s application to that of a loosely coupled version of the same application


Changes to the Data Access library ripple through the User Interface library as well andthe User Interface library can’t be deployed without the Data Access library. Even though the diagram doesn’t show it, there’s a dependency between the User Interface and the Data Access library. Assembly dependencies are transitive.

Note

Transitivity is a mathematical concept that states that whenever element a is related to element b and b is related to element c, then a is also related to c.

This transitive relationship means that because Mary’s User Interface depends on the Domain, and the Domain depends on Data Access, the User Interface depends on Data Access as well, which is exactly the behavior we’ll experience when deploying the application.

If we take a look at the dependencies between the projects in Mary’s application, we see something different:


Figure 4. The actual dependencies between the libraries in Mary’s application.


As you can see, even in Mary’s tightly coupled application, the entry point depends on all libraries. Both Mary’s entry point and the Composition Root of the loosely coupled application have the same number of dependencies.

The total number of dependencies between all modules in Mary’s application is six. This is one more than the loosely coupled application. Now imagine an application with dozens of projects. It’s not hard to imagine how the number of dependencies in a tightly coupled code base explodes compared to a loosely coupled code base.

IMPORTANT

By writing loosely-coupled code that applies the Composition Root pattern we can lower the number of dependencies. This allows us to replace complete modules with different ones, which is much harder in a tightly coupled code base.

The Composition Root pattern applies to all applications developed using DI, but only startup projects have a Composition Root. A Composition Root is the result of pushing the responsibility of the creation of dependencies to consumers. Two patterns can be applied to achieve this, Constructor Injection and Property Injection. Constructor Injectionis the most common, and should be used almost exclusively.

That’s all for this article.


If you want to learn more about Dependency Injection, check out the book on liveBook here.