Dependency Injection Best Practices in an N-tier Modular Application

Introduction

Microsoft Unity is one of the most popular tools to implement Dependency Injection (DI).

Note: If you are not familiar with Inversion of Control (IoC) and DI, you can find more detail about them on Martin Flower's blog here Dependency Injection. I don't believe IoC is better explained anywhere else.

In a typical modern ASP.NET MVC Web Application, you will find a three tier layered architecture with units of isolation dependent on each other as illustrated in Figure 1.

A DI framework allows you to inject the dependencies, and in our web application to be precise, it allows you to inject the Data Layer contracts in Business Layer and the Business Layer contracts in presentation without having to create concrete instances of the implementation.

Problem

The DI container creates a dependency graph (Composite Root) between various registered objects. It requires that we register our concrete implementations with the DI. The host applications like ASP.Net MVC or WCF should refer to all the assemblies so that DI container can register all the dependencies during the application start phase. It means the host application will have direct access to various implementations of the business or data layer. This leads to few major issues as it is not only violating the architectural rules of layer separation but also it can lead to bad coding practices when a developer starts consuming the data layer or entity models directly in the presentation layer. This blurred layer separation can also make it hard for a compiler to check the architectural rules at compile time.

Further if the application lacks any capacity for discovering components on its own, then it must be explicitly told which components are available and should be loaded. This is typically accomplished by explicitly registering the available components in code or a configuration file. This can become a maintenance issue.

Let's discuss this in detail with an example:-

Figure 2: Implementation details of Business and Data access layers

In Figure 2, the home controller in the presentation layer depends on IUserDomain (a business layer interface) whose implementation UserDomain is internal to the business layer assembly. The UserDomain depends on IUserData whose implementation UserData is again internal to data access assembly.

The DI container is in the ASP.Net MVC application and it is in charge of instantiating all of the registered types but in order to do so it needs access to all internal concrete implementations of IUserDomain or IUserData. This would mean we need to make those implementations public and reference all of the assemblies in the host application.

Let's Dive into the Code

I have created a sample ASP.Net MVC 4 application that uses Unity.Mvc4 to implement unity container and dependency resolver.

Visual Studio Solution design:

Figure 3: Sample ASP.Net MVC 4 Application

Common: UnityDemo.Common. It will have all the shared resources.

Domain: It is collection of modules. Each domain or module is fully capable of handling any business function. So it should know its data access layer (shared among domains or independent). Security in this case is one of the domain or module.

UnityDemo.Security: A business layer for security module or domain.

UnityDemo.Security.Data: Data access layer for security domain.

Web: It has ASP.Net MVC 4 application

Some Important Pieces of the Above Solution

IUserDomain: Business layer façade. The 'Domain' suffix means an independent business function/area. The entire business layer can be divided into multiple Domain layers, which may share or have their own data layer.

3. Direct references of business and data layer assemblies in the presentation layer to register concrete public types:

Figure 4: Ref in Web Problem

Let us now explore an approach to make this better.

Solution

If you are not using a DI container, you won’t need to reference data access assemblies of any module which might also contain Entity Framework (EF) models in an MVC application. You could have just referenced business layer assemblies. But this would mean that you lose some of the benefits of decoupling. We know that at the end of the day all assemblies would be included in the bin folder of MVC application, but the problem is the accessibility of concrete implementation of data or business implementations in the presentation layer. Let's dive into a technique where we can solve this problem by keeping concrete implementations internal while still benefiting from DI.

We can leverage the power of MEF. The Managed Extensibility Framework or MEF is a library for creating lightweight, extensible applications. It allows implicit discovering of extensions via composition without any configuration. An MEF component, called a part, declaratively specifies both its dependencies (known as imports) and what capabilities (known as exports) it makes available. When a part is created, the MEF composition engine satisfies its imports with what is available from other parts.

This approach solves the problems of hard-coding the references or configuring them in a fragile config file. MEF allows applications to discover and examine parts by their metadata, without instantiating them or even loading their assemblies. As a result, there is no need to carefully specify when and how extensions should be loaded. For more detail on MEF read Managed Extensibility Framework. MEF is an integral part of the .NET Framework 4.

Let's see how to implement MEF along with Unity to solve this problem:

We can define an interface IModule and its implementation ModuleInit in all the business or data layer assemblies. This interface will help to identify ModuleInit, and ModuleInit class will be responsible to register all internal implementations against defined interfaces. Let's dive straight into the code:

We will need reference of System.ComponentModel.Composition assembly to implement MEF.

To invoke registration: Identify the ModuleInit classes and call Initialize:

For this, we can define a static class ModuleLoader in Common project and call it from Bootstrapper of the host application. This class will have a method called LoadContainer, which will find all the MEF parts using import definitions. All the ModuleInit classes have export defined for the type IModule. In this way we can find all the modules and register them by calling the Initialize() method of ModuleInit classes.

Find all ModuleInit and call initialize to register types in various modules [MEF implementation]:

Advantage/Benefits

1. Automatic registration of type exposed by any module. If we need to change implementation of any interface consumed by MVC Web application or a business layer, we can just define a new assembly and add to the bin folder of the Web application.

2. This application can easily be extended by combining various modules, which can be registered to Unity by MEF extensibility support during runtime.

3. All implementations of module or data layer remain internal to respective assemblies. So, it can't be misused (See the breaking code due to absence of domain and data): The concrete types are not accessible in controller now.

4. No reference of Data layer in MVC application: The business layer (UnityDemo.Security) is referenced only because we need all the assemblies in bin folder of MVC app, but it won't harm as we have all implementations in business layer internal to that assembly.

Figure 5: Ref in Web Solved

Source Code

I have developed this sample application; you can download it here. You can also find it on github. This application is in ASP.Net MVC and demonstrates the technique discussed above. The source code is not perfect in all sense as its primary focus is to demo this MEF based unity container initialization.

To run this application, you would need Visual Studio 2010 with MVC 4 installed. Once you open the solution you can go to Manage Nuget Package on UnityDemo.Web project and click following button to restore the missing packages.

Figure 5: Restore Package

Conclusion

In this article, you have learned a different technique to configure DI (Dependency Injection) using unity container and MEF. This technique solves a couple of problems. First, it helps to keep the business or data layer implementations internal to respective assemblies and second, the architectural rules are not violated while utilizing the benefits of a DI container. This leads to much cleaner modular application which can be extended quite easily.

About the Author:

Manoj Kumar is a Principal Consultant with Neudesic with significant experience of 9 years in designing and developing enterprise scale solutions. He poses sound knowledge of SDLC, software architecture and design practices such as ATAM, architecture analysis, SOA/SOD, design patterns, distributed architecture, cloud computing, ESB like Neuron/BizTalk, and OOP/OOAD. He keeps special interest in technologies and looks for better possibilities in software developments. He blogs regularly on his popular technical blog and has also published articles on popular technical sites like CodeProject. His screencasts published on YouTube are quite popular. Most recently he has been actively working on building apps and evangelizing development practices on Windows 8 and Windows Phone 8.

IT Solutions Builder
TOP IT RESOURCES TO MOVE YOUR BUSINESS FORWARD

Which topic are you interested in?

Mobile

Security

Networks/IoT

Cloud

Data Storage

Applications

Development

IT Management

Other

What is your company size?

What is your job title?

What is your job function?

Searching our resource database to find your matches...

Please enable Javascript in your browser, before you post the comment! Now Javascript is disabled.

Advertiser Disclosure:
Some of the products that appear on this site are from companies from which QuinStreet receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. QuinStreet does not include all companies or all types of products available in the marketplace.

Thanks for your registration, follow us on our social networks to keep up-to-date