Although ASP.NET MVC has been here for years the discussion about fat and thin controllers continues. There are guys who have strong faith in fat controllers and guys who believe more in thin controllers. Having seen both types of controllers and worked on them in practice I wrote this post to share my experiences with you.
What is fat controller?
First let’s agree on what is fat controller. I want to include here also some exceptional cases so there’s no chance you start fixing fat controllers with wrong medicines. Controller can be fat three different ways:
- there is a lot of code that should be in some other layer,
- controller has many methods for AJAX responses,
- controller has many methods to return JSON data.
NB! AJAX responses with partial views are there to support UI. If we have complex view then there’s nothing to do – our controller will grow and I don’t handle this case as a usual fat controller case. You can move these methods around in presentation layer but they still remain ballast with same weight.
Problems with fat controller
Fat controllers come usually with problems that are not very easy to solve or get around.
- It’s very hard to get controller and its actions under test harness
- Parts of business logic in one controller are often repeated in other controllers
- UI developers who work with controllers have too much technical overhead to handle – they are usually not very familiar with deeper layers and their specifics
I think these problems are bad enough for even smaller projects to motivate you to do something about it.
Moving code to other layers
If your controller has a lot of code that is about retrieving data, preparing data for saving etc then likely you are in situation where you need some layer between your controller and data access layer. It’s also possible your controller is dealing with business logic and you don’t have layer for it.
If your controllers communicate directly with data access layer (DAL) or object-relational mapper (ORM) then they tend to do too much work. Besider user interface (UI) logic your controller contains also data access logic and business logic – it’s three different things in one place.
So, let’s make some points here:
- Business logic is carried out by classes in business logic layer (BLL)
- DAL is behind BLL and UI has no reason to have direct access to DAL
- For more complex cases you should use Servile Layer pattern defined by Martin Fowler. In this layer you implement use cases of your application.
Service Layer pattern is given on image on right. In the center of layers is DAL. It is surrounded by Domain Model – another pattern defined by Martin Fowler. Domain Model is your BLL in classical means.
Around domain layer we see Service Layer that is there to provide well defined use case implementations for its clients. Client can be any application or service that needs support for these use cases.
It may seem like complex task to do but you will be happy later when it’s done and you see how much better is your situation when you don’t have to live with code where responsibilities are mixed.
For practical advice and samples please head to my blog post Moving code from controller action to service layer. You can find out more about Service Layer pattern from book Patterns of Enterprise Application Architecture by Martin Fowler.
Moving code to Web API
If your views make requests to controller just to get and save JSON-based data then you should introduce API controller for this kind of requests. It’s possible you need to share those methods between multiple controllers and to avoid repeated code and the situation where view of one controller makes requests to another controller you better introduce API controller so you don’t have to repeat code or introduce hard to see dependencies.
API controllers are designed to support this kind of scenarios and they are not hard to write. If you don’t like JSON-format for some reason then API controllers support also XML and there is also good support for content negotiation.
Using API controller you make your controller thinner and easier to test. For API controller you can write separate set of tests if needed. Also you may share your API with other sites and applications that want to use the functionalities in your system.
Fat controllers are problematic and when they grow then also problems related to them will grow. Often by higher magnitudes. Sooner or later we reach to the point where controllers are hard to handle and manage and code gets more and more ugly. Correct layering and avoiding duplicate code is your way out from the bad situation. Avoiding fat controllers ends up with cleaner and easier to handle codebase with no duplicated code plus your code is testable again.