Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

An Idea of a Three Tier Application using IoC (Inversion of Control), DI (Dependency Injection), and Perfectly Decoupled Layers

4.50/5 (2 votes)
12 Oct 2012CPOL5 min read 25.3K  
A very basic concept in developing a three tier application

Introduction

Let's start with a very basic conception in developing a three tier application. These kinds of applications normally have three layers: Data access layer (DAL), Business Logic Layer (BLL) and the View or Presentation layer. The organization of the layers is like the picture below.

Image 1

Figure: Old Three tier

In this organization, the BLL is directly dependent on DAL and the View layer is directly dependent on DAL and BLL layers. Here, say for example, a ProductService class in BLL will directly use the ProductRepository class in DAL layer like this:

C#
public class ProductService 
{
    ProductRepository pr;
    int getProductCount()
    { 
        pr = new ProductRepository();
        return pr.getSqlProductCount();
    }
}

This is a problem; because with the direct initialization of ProductRepository in ProductService class makes them tightly coupled. At this point, suppose you need to change the repository to OracleProductRepository, then you need to rewrite the ProductService class and compile again. Moreover, it is also not possible to test the ProductService object without the ProductRepository object.

The same happens to the objects of the View layer which directly uses the objects of BLL and sometimes DAL. This makes View layer tightly coupled to the BLL and DAL layer which thereby restricts the View layer to test without exactly that BLL and DAL layer.

Actually our problem is: all objects instantiation of DAL layer are scattered among all the objects of BLL layer and all the BLL layer objects instantiation are scattered among all the objects of View layer objects. To solve this – somehow we will have to control the object instantiation from one center.

The Remedy

The remedy lies in IoC and DI. So lets get familiar with those.

IoC

Firstly, to get rid from this problem, we will have to use the IoC – Inversion of control. We will have to move the control of instantiation-of-objects to a separate entity – a separate object which we will call Composition Root.

DI

Secondly, notice the Figure: Old Three tier. There, the BLL depends on DAL because objects of BLL need to instantiate the objects of DAL layer. If we can remove this dependency, than we can restrict the objects of BLL to directly access the objects of DAL. But objects of BLL need to access the DAL objects – how will they do that? The answer is by Dependency Injection and Interface.

Notice the picture below.

Image 2

Figure: New relation between BLL and DAL

Here, the BLL and DAL have their own interfaces and BLL is not depending on DAL, rather DAL is depending on BLL. Later, we will see the advantage of that.

Notice also that there is a separate object CRoot that will instantiate all the objects in DAL and BLL. Here, we have moved the control of instantiation of all the BLL and DAL objects, thus implemented Inversion of Control(IoC).

CRoot will also inject the DAL objects in the BLL objects while instantiation. This is called Dependency Injection (DI). Interface will be used to do that.

Have a look at the ProductService class of BLL and follow the comments:

C#
public class ProductService : IProductService
{
  // Dependency of the DLL object will be injected here
  ISqlProductRepository sqlPrdRepo;

  // Constructor
  public ProductService(ISqlProductRepository _r)
  {
    // Injecting Dependency
    sqlPrdRepo = _r;
  }

  // Other functions coming...
}

Now the DAL object that will be injected will simply implement the Interface:

C#
public class SqlProductRepository : ISqlProductRepository 
{
    // other functions for database query
}

Notice in the Figure: New relation between BLL and DAL that DAL depends on BLL – this is because sometimes DAL needs to populate the POCO (plain old CLR objects) objects or domain objects of BLL. DAL will fetch data from the Repository objects or Data layer objects and will pass these data to the BLL POCOs.

A sample POCO is as this and resides in BLL.

C#
public class Product : IProduct
{
    public string Name { get; set; }
    public int Id { get; set; }
}

And this POCO is populated in the DAL layer by SqlProductRepository object like this:

C#
public class SqlProductRepository : ISqlProductRepository 
{
    public IEnumerable getSqlProductList()
    {
        // Making a list of POCO 
        var _l = new List()
        {
            // In practical projects this data will come from DB
            new Product(){ Name = "Xion Precessor" , Id = 21},
            new Product(){ Name = "Ci7 Precessor" , Id = 20},
            new Product(){ Name = "Seleron Precessor" , Id = 13},
            new Product(){ Name = "Ci5 Precessor" , Id = 17}
         };
        //returning the list to BLL
        return _l;
    }
}

Finally, this Product list is passed to layer upwards (i.e., View layer) by the object ProductService in BLL like this:

C#
public class ProductService : IProductService
{
  // Dependency of the DLL object will be injected here
  ISqlProductRepository sqlPrdRepo;

  // Constructor
  public ProductService(ISqlProductRepository _r)
  {
    // Injecting Dependency
    sqlPrdRepo = _r;
  }

  public IEnumerable GetSqlProductList()
  {
    //  Using the dependency get the Product list and
    //  return to layer upwards
    return sqlPrdRepo.getSqlProductList();
  }
}

Here, all the interfaces like IProduct, IProductService, and ISqlProductRepository will be resided in Interface layer. These interface layers are separated so that all the other layers (i.e., View layer) can efficiently use the interfaces rather than the original object.

Finally, comes the CRoot object where the BLL object – ProductService is instantiated and the DAL object – SqlProductRepository is injected in it.

C#
public class Root
{
  // A property as IProductService holding the ProductService  
  public IProductService ProuctService { get; set; }

  // Constructor
  public Root()
  {
     // Instantiating the DAL object
     ISqlProductRepository _repo = new SqlProductRepository();

     // Instentiating the BLL object and 
     // inject the DAL object in it
     this.ProuctService = new ProductService(_repo);

     // ... Other DLL and BAL objects instantiating       
  }
}

Now is the time to integrate the View layer or Presentation layer. This layer should only depend on the Composition Root (i.e., CRoot) and also should be perfectly decoupled from the BLL and DAL.

See the final diagram:

Image 3

Figure: Final View,BLL and DAL

As you see, the View layer depends on CRoot and from there, it will get all the necessary BLL and DAL objects. View layer will not use these objects directly, rather it will use interfaces of those objects. Thus, it is perfectly decoupled from the DAL and BLL layer.

The view layer could be anything from Console Application to Web or Desktop or Mobile Application and for simplicity, I am showing a console application as a view. This view layer will ask the CRoot for an instance for IProductService. Then, it will use this IProductService to get the list of IProduct.

Code from the View layer:

C#
static void Main(string[] args)
{
  Root _root = new Root();
  IProductService _prdsrv = _root.ProuctService;
  List _l = (List)_prdsrv.GetSqlProductList();

  Console.WriteLine("The processor lists as bellow");
  foreach (IProduct pr   in _l)
  {
     Console.WriteLine("The product ID: " + 
       pr.Id.ToString() + " --- Name: " + pr.Name);
  }

   Console.Read();
}

There are significant advantages we will get from this design:

  1. We have centralized our object instantiation to CRoot object. So neither can you create DAL object in BLL layer nor BLL objects in View layer. Hence our code is highly maintainable and reusable.
  2. We have injected the DAL objects into BLL objects through constructor injection from CRoot object. Hence the DAL layer is less coupled with BLL layer. Some day, if we want to replace the SQL DAL layer with Oracle DAL – we’ll just have to modify the CRoot object. The new Oracle DAL will have to implement the interfaces.
  3. Our view layer is also loosely coupled with the BLL and DAL because it is using the Interface layer to handle BLL and DAL objects.
  4. The application is easy to test. For example, we can easily make a dummy DAL layer and use it in application and test.

Possible Upgrade

Up to now, we have successfully created an application where the codes are highly maintainable and reusable, the layers are perfectly decoupled and easy to test individual layer. For further improvement, we can introduce a Container in the CRoot layer. The container can be any sort of – from Unity, Castle Windsor, StructureMap to Spring.NET.

Final Words

The design concept is totally my own – so I will appreciate comments and criticism.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)