Do something SOLID with ASP.Net Core
Introduction
Most of us follow the principles of SOLID during software designing. We can ensure the scalabilty of our own application by implementing own pattern of design, but these principles we can adopt as it's already tested for different size of applications.
We don't need to know about SOLID to develop application using ASP.Net Core. I want to say thanks to developers of ASP.Net Core or .Net Core, those are develop such a framework where we can customize anything. We can customize, if it's really required or we can use the default pattern. This article is not to cover all the aspect of ASP.Net core, but how to use dependency injection and follow the SOLID to improve the quality of your coding.
Who are familiar with SOLID principles and exercising best practices for long time they can directly visit the links in useful links section of this article. I will explain that why should we use this type of best practices in following paragraphs.
Lets start with a problem
We need to calculate charges for tax during generate any sales invoice. We are familiar with different type of taxes in India. Such as service tax (ST), value added tax (VAT) and central sales tax (CST). Lets say we have a requirement to create a controller class in MVC application, which will be responsible invoice for related operations. Now a days our government proposed to introduce a new system for tax called goods and services tax (GST). This will replace all other taxation process and make it simple. In this situation we got this requirement and client wants to implement the new taxation system, whenever it will be implemented.
Our common approach -
As a developer we will write a method within our controller class or invoice module to calculate tax related charges. Otherwise, we will create another class for taxation and implement the methods to calculate charges then add a reference in controller.
What's wrong with it?
First of all we violate the single responsibility principle, if we implement the tax calculation process in invoice controller or module class. Invoice and taxation both are different, but bounded with "has-a" relationship. Simply it's a time consuming process to load a large file and search for the method, if I need to fix something.
Also we need to make changes in our code when the new taxation process will be implemented. Why it should be wrong? it's a basic practice to make changes in existing code when business logic or requirement will change. Yes, it's a basic practice when we found any bug in our code and we found that the implemented process was wrong. It shouldn't be a best practice because changes in existing code will increase the possibility to introduce new bugs in others dependent sections. It's don't suggested by open/closed principle.
On the other way, if we create a separate class for taxation and use it from invoice controller. Then we will introduce dependency upon concretion in invoice controller for taxation class. We have to make changes in invoice class, if we want to replace the taxation class. It will violate the dependency inversion principle.
Now think in a different way
We can create an interface for taxation and add reference of this interface in the invoice controller. Now we are free to change the old taxation class with new one, when GST will be introduce.
But, how can we create the instance of old or new taxation class. It will again violate the open/closed principle, if we need to change the codes in invoice controller, while introduce the new class for taxation. Here, we can use dependency injection to pass instance of dependency from outside. Dependency injection is not new, but it's good enough to implement for increase scalability of our applications. I was posted an article on getting started with unity framework. You can go through my old article, if want introduce yourself with dependency injection and how can be it helpful for scalability of application.
Lets conclude with ASP.Net Core -
Previously we have to use unity framework or some other tools for dependency injection. But, .Net core come with an existing support for dependency injection within the framework. So, you can make some small changes in configuration section and ASP.Net core will mange the rest.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
------
//you need to add your service here to switch from old to new taxation class
services.AddTransient<ITaxation, OldTaxation>();
----------
}
In future, you need to change a single line
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
------
//you need to add your service here to switch from old to new taxation class
services.AddTransient<ITaxation, NewTaxation>();
----------
}
Useful links
Few final tips
Old taxation process may have extra methods, which will no longer required for new taxation process. So, if we want to mark few methods in new class as "Not Implemented", then it will again a bad practice. Program will try to access those methods, without knowing that the new class has implemented those method or not. All the child classes should act in similar manner, if we implement same interfaces as because of they are substitute of each others. You can check liskov substitution principle for more detail.
So, we can implement the interface segregation principle to avoid the above problem. Invoice controller can work with any taxation class but other section of the application should access the same class for different purpose.