How to avoid fat controllers in ASP.NET Web API (.net framework)

Ali Ashoori
Level Up Coding
Published in
8 min readJan 11, 2020

--

This has been assumed that some basic knowledge and experience of ASP.NET WEB API, C#, and SOLID principals are already acquired as this article will not focus on these but rather a solution to avoid writing big controllers.

Background

Building rich web-based applications using ASP.NET WEB API is no longer something new and it has been reliably around for years now. Implementing RESTful APIs and building up powerful services communicating over HTTP and exchanging in SOAP or JSON, configuring custom API routes, applying custom filters against controllers and actions, all and all are just primarily parts of this framework which have resulted in massive popularity through the entire market.

But, when it comes to coding and scripting out the logic, that, is another story and regardless of how powerful the target framework is, writing out proper, cohesive, and clean code is more a matter of techniques, knowledge, and of course experience.

The challenge

One of the challenges that could potentially make our Web API Controllers unnecessarily big is the number of objects that each controller needs to embrace so that its actions can utilize them to invoke the target operation exposed via those particular objects. Basically, the more of these objects our controller depends upon, the more growing and messy it will likely to be.

One may immediately think of IoC and applying DI pattern in order to sort this problem out. But, that, unfortunately, takes care of totally another concept (as we all probably know) which is instantiating objects via another responsible component so that more decoupling will happen in an application which is quite appreciated. But, those objects should get injected into the consumer somehow, either through the constructors or properties, or even methods based on the needs of the program and of course the IoC library in hand. And, this is when the controllers get noisy.

Throughout this article, this has been attempted to demonstrate this problem via some code snippets and then to consider a solution-driven approach to tackle this problem and to actually abstract away all of those objects on which our Web API Controller depends.

The Scenario

The practice scenario here is to implement Create, Delete, and Update APIs using ASP.NET and C#. In favor of focusing only on the article's purpose, the repository database components have left with no implementation and they only expose interfaces and operations through which the Create, Update, and Delete functionalities can take place.

In this example, we have been asked to apply the above operations on a Student object. The following will show the main participants of this scenario one by one through code snippets and then will go into more detail about the problem and the solution.

The Participants

First and foremost, we need to have our Student model class.

The Student Entity Model

Figure 1 — Student Entity Model

The Repository Classes (each of which to be as a separate class file)

Figure 2 — Student Repositories

Now, some may ask why having these many classes and interfaces separately and not having them all wrapped within a single StudentRepository class which even could be implemented as a Generic one so that it would cover any other possible entity classes in the application. Well, that sounds like a good question but actually not a good practice, especially when it goes to an actual production level. Although this discussion goes out of the scope of this article, some of the reasons can be pointed out quickly. First and foremost, having all operations within one single repository class could violate the SRP. Besides, what would happen if our application requires more than just Create, Delete, and Update? Then, we would have to implement every single new operation within our very lengthy repository class which of course could bring all sorts of problems; from difficulties in unit testing to maintenance and anti-pattern problems.

Putting the above discussion aside, we now need to have our Web API Controller in which we write our Create, Update, and Delete actions that would execute the desired functionalities against our database. We will use Autofac for Web API in order to practice IoC in our program. For more information, please read through this website.

So, let’s quickly see how to register our types using this IoC framework.

Figure 3 — Global.asax.cs

Now that classes with their interfaces have been registered, we just need to inject them through the controller constructor and utilize them as needed. The controller class looks like the below.

Figure 4 — Student Controller with three objects injected into the constructor

And that’s it. These are all the basic participants in this scenario.

The Problem

But, have you noticed the problem that already is there? Yes, that’s right — our controller now is very likely to extend and gets bigger as it may need to have more dependencies injected into its constructor (if not already enough). In a real application, this can easily happen and make every single controller more messy and lengthy. Another kind of problem here is that the very first component that our controller communicates with is our repository classes which from the design point of view is not like the best practice. Indeed, we better to have some handlers who actually would handle our incoming requests and pass them to any successor which in our case let’s say are the repository classes.

So the question is what ways are out there to avoid such a problem and always keeping our controllers as sexy and as thin as possible?

The Solution

One way that we will discuss in this article is to delegate the responsibility of processing the API requests into another component (or participant) and try to train that component to resolve the desired handler based upon the incoming request from the API. In this way, our Student Controller (or any other one in the program) would probably only depend on this newly joined component (processor) and the controller only needs to send the request out to this processor so that it will take care of the rest itself. That sounds cool so let’s see how we can implement this.

In order to step into the solution, we may need to firstly have a very basic understanding of the CQRS Pattern which stands for Command Query Responsibility Segregation. Martin Fowler very well explained this at his website here but just to come up with an initial idea CQRS believes that the requests are either Commands or Queries. In our simple scenario, we can conclude that Delete, Update, and Create are basically the commands that the user can make and retrieve the data from the database would be our queries (if there was any).

But, how is this going to help us to make our controller in shape? Let’s begin with figuring out our command classes.

The Commands

Now that we have the concept of commands we need to reflect that in our codebase as well. According to what briefly mentioned above, we will have the following commands in our application.

Figure 5 — The ICommand interface
Figure 6 — Register New Student Command
Figure 7 — Update Student Info Command
Figure 8 — Remove Student Command

So far so good! We have now some commands that are the actual requests coming from the users. But why not having public properties with usual get and set? Well, the answer commands are by nature messages which are immutable. Unfortunately, discussing this would go out of the context and that is why we may just better end it here. But, this link may come helpful to find out more about this reason.

These command classes are actually one of the factors based on which our aforementioned component would utilize in order to resolve the target handlers, which then, would invoke the desired repository to apply the command against the database. So, let’s have a look at our command handlers and how they should get implemented.

The Command Handlers

Figure 9 — Command Handler Interface
Figure 10 — Register New Student Command Handler
Figure 11 — Update Student Info Command Handler
Figure 12 — Remove Student Command Handler

By this point, we have a new set of classes exposing our commands and command handlers. These are the ones that indeed should place between the repository and the controller class. But again, if we just try to replace them we would still have the problem of fatty controllers. This is when the other component comes in play which would be responsible to sit between our student controller and the actual command handlers so that our controller would be needless of having all the handlers injected.

In fact, the basic idea is for the controller to send out the command objects to this processor so that it will then resolve or find the associated handler according to the contract (the command handler interface) and would eventually ask the handler to take care of the request from that point on.

So, let’s see now how this command resolver would look like and that how we can implement this once only so it can deal with the future added handlers as well.

The Command Processor

Figure 13 — Command Processor responsible for resolving the handlers at runtime

Lovely!

We now have a processor that would be the middleware between the controller and handlers which is able to resolve the handlers (that all, of course, follow the command handler contract) using the powerful feature of .NET, Reflection.

Next is to first register our command handlers and the processors from within the Global.asax file and then to inject them directly into the student controller for further consumption. We just need to update the file in the following format.

The Global.asax

Figure 14 — Registering Handlers in Global.asax

Now, let’s see how different our controller would look like using this solution.

Figure 15 — Controller with only one dependency injected regardless of how many different actions it exposes

Beautiful isn’t it?! We can now add as many as commands and command handlers to our program and our new controller would be needless of knowing them and having every single of them injected into the constructor. This solution also somehow could be respecting the Open Close Principle which says a class should be open for extension but close to modification.

And that’s all about it!

--

--