Why Interface For Decoupled System? A Note On How To Design Decoupled System in ASP.NET MVC

, , No Comments

An Interface

At first, interfaces came into focus because there was no multiple inheritance supported by C#, meaning you could not inherit from multiple classes, but you could implement multiple interfaces. The interfaces are grouping of object in terms of behavior.

An interface contains only signatures. There is no implementation, only the signatures of the functionality the interface provides. An interface can contain signatures of methods, properties, indexers & events.

Interface doesn't do anything like classes and abstract classes, it just defines what the class will perform if some class inherits it. An interface can also be inherited from another interface.

If we take an example of USB (Universal Serial Bus), it's an interface which has signature of Connection functionality. The USB interface only knows I have to connect with Desktop Computer or laptop or anything, but it does not know what implementation will come to connection. It may be Pen drive (mass storage), it may be Printer(COM Ports).

Let's see the concept in a programmatic way.

Using the Code

An USB Interface

public interface IUSB
{
    public void MakeConnection();
}

The Pendrive Class

public class Pendrive: IUSB
{
    public void MakeConnection()
    {
        //The code for connecting pen drive to desktop, laptop or other
        throw new NotImplementedException();
    }
}

The Printer Class

public class Printer: IUSB
{
    public void MakeConnection()
    {
        //The code for connecting printer to desktop, laptop or other
        throw new NotImplementedException();
    }
}

Decoupled System

Now the decoupled system means the way of defining the architecture of project that one module cannot depend on another module. The system should depend on abstraction rather than implementation.

In a realistic way, suppose I am currently designing the MVC project with Microsoft SQL Server but if in future I have to change the database to MySQL. In this case, I just need to change the Repository Implementation not a Controller.

So my Controller should be implementing the abstraction/interfaces of Repository. When I change the implementation of Repository, I just have to change it to new implementation, rest nothing will be changed.
private readonly IRepository _repository;
public ActionResult Index()
{
    _repository = new MSSQLRepository();
    //or
    //_repository = new MySQLRepository();
}

Decoupled System Using Interface By Realistic Example

As I am a developer, I got one requirement from client. The requirement is that I have to get current exchange rate by currency. So I have to Pass USD and Get current INR Rate or Current EURO rate. There are various web services that offer this functionality.

So consider providers ABC, DEF & PQR provide these web services, but they charge different rates. So my client said to me at first we will try ABC's Service, but if we find it's not reliable, then we have to change it at a later stage.

So let's consider the dirty code first:
public class ProductController : Controller
{
    public async Task<ActionResult> DisplayProduct()
    {
        Product p = new Product();
        p.Name = "Test Product";
        p.Type = "USD";
        p.Price = 5;
        
        //All ABC Service code which returns USD to INR 
        double INR = 
        p.Price = p.Price * INR;
        return View(p);
    }
}

Now let's assume that I am going to use this dirty code implementation directly in the controller. It has some disadvantages.
  1. If I implement service code directly to controller, I have to change it in all controllers which need currency conversion functionality. For this demo, I have only 1 Controller and 1 Action, but in a real world application, we may have lots of Controllers and Actions.
  2. To change the code in each and very controller which needs conversion functionality, it's very frustrating, and it's the opposite of Good Programming Standard. As per Programming Standard, we have to reduce repetitive efforts.
Now, I have to design some logic in a decoupled way, So when client says we have to go with another service provider, I don't have to change much in my Controller. But the problem is how to design the decoupled logic and then Interface comes in focus.
The design is as follows:
  • What is important here is, I have to get current exchange rate by passing currency.
  • So I created one ICurrencyExchangeService Interface.
  • Then, I just defined the function signatures in interface, e.g., GetCurrentINR(string currency); GetCurrentEURO(string currency);

ICurrencyExchangeService Interface

public interface ICurrencyExchangeService
{
    double GetCurrentINR(string type);
    double GetCurrentEURO(string type);
} 
  • Now I created ABCCurrencyExchangeService class which inherited from ICurrencyExchangeService
  • All functions [defined in interface] will be implemented here as per the ABC Service documentation

ABCCurrencyExchangeService Class

public class ABCCurrencyExchangeService : ICurrencyExchangeService
{
    public double GetCurrentINR(string type)
    {
        double exchangeRate = 0.0;

        //Call the ABC web service with appropriate parameters as per the  documentation. 
        //Assign converted rate to exchangeRate variable

        return exchangeRate;
    }

    public double GetCurrentEURO(string type)
    {
        double exchangeRate = 0.0;

        //Call the ABC web service with appropriate parameters as per the  documentation. 
        //Assign converted rate to exchangeRate variable

        return exchangeRate;
    }    
}
  • Now I am using ICurrencyExchangeService in my Controller.

ProductController Controller [MVC]

public class ProductController : Controller
{
    ICurrencyExchangeService  _currencyService = new ABCCurrencyExchangeService();

    public async Task<ActionResult> DisplayProduct()
    {
        Product p = new Product();
        p.Name = "Test Product";
        p.Type = "USD";
        p.Price = 5;
        //But we have to show INR prices
        double INR = _currencyService.GetCurrentINR(p.Type);
        p.Price = p.Price * INR;
        return View(p);
    }
}
  • All my code is going well but after few days, client says the service is not reliable. We have to migrate it to another service, i.e., DEF Service.
  • Now what to do? I have 2 options:
    1. Change the ABCCurrencyExchangeService Class implementation as per the DEF Service documentation. But it's against the name standard because we are implementing the DEF service under the class name ABCCurrencyExchangeService.
    2. Also one more disadvantage is suppose after some days, if client says we have to revert our old service that is ABC, then we have to again implement ABC service.
  • The second and good option is we will create new class DEFCurrencyExchangeService which inherited from ICurrencyExchangeService Interface and I implement all the methods as per the DEF Service.
public class DEFCurrencyExchangeService : ICurrencyExchangeService
{
    public double GetCurrentINR(string type)
    {
        double exchangeRate = 0.0;

        //Call the DEF web service with appropriate parameters as per the  documentation. 
        //Assign converted rate to exchangeRate variable

        return exchangeRate;
    }

    public double GetCurrentEURO(string type)
    {
        double exchangeRate = 0.0;

        //Call the DEF web service with appropriate parameters as per the  documentation. 
        //Assign converted rate to exchangeRate variable

        return exchangeRate;
    }
    
}
  • Now I just have to change the one line in my controller. The repository is now pointing to DEFCurrencyExchangeService Class. So my new Product Controller looks like:
public class ProductController : Controller
{
    //ICurrencyExchangeService  _currencyService = new ABCCurrencyExchangeService();

    ICurrencyExchangeService  _currencyService = new DEFCurrencyExchangeService();

    public async Task<ActionResult> DisplayProduct()
    {
        Product p = new Product();
        p.Name = "Test Product";
        p.Type = "USD";
        p.Price = 5;
        //But we have to show INR prices
        double INR = _currencyService.GetCurrentINR(p.Type);
        p.Price = p.Price * INR;
        return View(p);
    }
}
  • I just change one line and our new service is started. Also, after some days, if my client asks me to revert back to ABC service again, I have to change only 1 line.
  • If you are plan to use it with Dependency Injection container so it gives us flexibility at whole new level. You don't have to change anything in Controller. We have to just change in dependency injection container and it's done.

Using Unity as Dependency Injection Container


If you don't know about DI or how to use it, please check the following links:

http://www.codeproject.com/Articles/786332/ASP-NET-MVC-with-Unity-Dependency-Injection
http://www.asp.net/mvc/overview/older-versions/hands-on-labs/aspnet-mvc-4-dependency-injection

Using the Code

UnityConfig.cs Class Configuration (Located in App_Start\UnityConfig.cs)

public static class UnityConfig
    {
        public static void RegisterComponents()
        {
            var container = new UnityContainer();

            // register all your components with the container here
            // it is NOT necessary to register your controllers

            // e.g. container.RegisterType< ITestService, TestService >();
            //container.RegisterType< ICurrencyExchangeService , ABCCurrencyExchangeService >();
            container.RegisterType< ICurrencyExchangeService , DEFCurrencyExchangeService >();

            DependencyResolver.SetResolver(new UnityDependencyResolver(container));
        }
    }
  • The above code uses unity DI container and we have just changed mapping. Previously, ICurrencyExchangeService Interface was pointing to ABCCurrencyExchangeService Class and now it's pointing to DEFCurrencyExchangeService Class. The dynamic object creation is done by UNITY DI.
  • Now our new Product Controller will look like:

ProductController Class:

public class ProductController : Controller
{
    private readonly ICurrencyExchangeService  _currencyService;

    public ProductController(ICurrencyExchangeService  currencyService)
    {
        _currencyService = currencyService;
    }

    public async Task DisplayProduct()
    {
        Product p = new Product();
        p.Name = "Test Product";
        p.Type = "USD";
        p.Price = 5;
        //But we have to show INR prices
        double INR = _currencyService.GetCurrentINR(p.Type);
        p.Price = p.Price * INR;
        return View(p);
    }
}

0 comments:

Post a Comment