I recently stumbled upon a Library called FastEndpoints while doing research and it immediately struck me as something I wanted to learn more about. I had started experimenting with Minimal APIs at the time. While I liked that Minimal APIs lower the entry point for simple API creation and are significantly more performant than MVC Controllers, they quickly turn your program.cs file into a jumbled mess with anything beyond trivial. The desire for code organization quickly pushes towards finding a sensible way to orient the code.
In addition, I have been a huge proponent of using the Mediatr library over the last few years as it provides a clean mechanism for creating code in vertical slices that support versioning and are truly SOLID. Unfortunately the pattern I was using still relied on MVC Controllers which ended up being a less than SOLID horizontal layer that tied things together. I had looked at options to eliminate the controller but had not found a solution that I felt was better than what I was already doing.
Since FastEndpoints on the surface seemed to provide a good way to organize my code on top of Minimal API and addressed the concerns I had with Controllers combined with Mediatr I decided to dig in. After a quick evaluation I implemented a couple of small projects with FastEndpoints with great success.
Since I have some experience with FastEndpoints, I thought it would be a good exercise to go through my old notes and answer some of my initial questions when I first found FastEndpoints.
Initial thoughts on FastEndpoints:
- Interesting! seems like Mediatr, but eliminates the controllers.
- Does this make controllers an unnecessary layer of ceremony for building vertical slices?
Absolutely, you can create testable and SOLID vertical slices with FastEndpoints. In fact there is a Visual Studio template available to help create endpoints that have an encapsulated request, response, validator and delegate handler. This is very similar to the pattern I used with Mediatr but eliminates the Controller layer.
I have been using Alba to test my APIs for a while now and it worked perfectly with FastEndpoints. Alba wraps the ASP.NET Core Test server to create an in memory test host that allows you to write XUnit or NUnit tests against your API. This is a quick an easy technique that provides the best level of testing since it will detect unexpected changes to your contracts and includes middleware in the tests. Yes this is more of an integration test than a Unit test, but in most cases this is much more valuable than a unit test around a delegate handler.
For trivial projects with one or two simple endpoints, I might still consider Minimal API. That said, I think I prefer FastEndpoints in almost all cases because it includes a number of developer convenience features that make things like Swagger, Security, Caching etc much easier and just works out of the box.
While FastEndpoints are slightly slower than Minimal API, they are still much faster than MVC Controllers and the benefits of less configuration and code organization outweigh the small performance degradation.
It is surprising how fast Minimal API becomes unwieldy with the bloated program.cs
file and all of the verbose config around each endpoint that is required for production level APIs. FastEndpoints definitely addresses these downsides of Minimal API.
It is definitely faster than Controllers from a performance standpoint and a developer experience standpoint.
Performance benchmarks published by FastEndpoints here: https://fast-endpoints.com/benchmarks#head-to-head-benchmark
The benchmark shows 35k more requests per second than MVC controllers, which is slightly slower than minimal API.
From a developer productivity standpoint, once you get over the brief learning curve, you can be much more productive with FastEndpoints. There is much less code to write and configure and many things just work out of the box. It definitely throws you into the pit of success for building production ready services.
The patterns around FastEndpoints definitely support code that fits in your head. Vertical slices are easy to create in a SOLID and testable fashion and the level of encapsulation and organization is excellent.
The only challenge is that we are all used to controllers at this point and it can be a bit disorienting at first to work with FastEndpoints. In my experience this is quickly overcome and the initial discomfort is worth the outcome.
FastEndpoints definitely provide patterns that lend themselves to maintainable code. Each endpoint is a separate set of classes that are SOLID. Testing can be done against delegate handlers but I highly recommend using an in memory test host like Alba instead of testing the handlers.
API versioning, which is a key component to maintainable APIs, is also supported with FastEndpoints. In fact I think versioning might be a bit cleaner to manage with FastEndpoints since it avoids mixing and matching versions in the same controller class.
In addition, FastEndpoints reduces the amount of boiler plate code you need to write for configuring many production level functions of an API. This reduction in boilerplate results in less code to maintain.
This is a question that took some introspection. I came to the conclusion that in most cases, controllers are a warm comfy blanket that we've become accustom to. They usually end up with loosely related combinations of code that is arguably not SOLID.
The knock out punch for controllers in ASP.NET is the fact that MVC Controllers are one of the slowest performing options you can choose today.
In conclusion, I can't think of what the controllers provide that would be missed.
I came to the conclusion that Mediatr would be largely redundant in a project that uses FastEndpoints. This made me more sad than loosing controllers, but in the end I'm happy to embrace the fact that I get the benefits Mediatr provided with a lot less code that is better organized.
In a lot of ways you can think of FastEndpoints like Mediatr without controllers.
There were many unexpected benefits of Fas Endpoints that I don't think you can fully comprehend until you create a production ready API with FastEndpoints. There were several times in putting together an API that I found that configuration was streamlined.
Here are some notes I took of my impressions:
There were a few things I did not care for in my first experiences with FastEndpoints.
In my first experience the delegate handlers were limited to property injection and I found that less than desirable. Since that time support has been added for Constructor Injection, so that issue has been resolved.
I did not have luck with using records for the requests. I'm hopeful this has been or will be an added capability for FastEndpoints. I ended up deciding that record types was not a show stopper for me.
Update: using records for requests may work at this time. https://github.com/FastEndpoints/FastEndpoints/issues/49
Nesting request/response as a sub class did not work. Although that pattern did not seem as important without controllers. There is nothing in your code outside of the delegate handler that references the request/response so the utility of the sub classes are no longer necessary.
After getting some experience with FastEndpoints I'm convinced this is my preferred pattern going forward. I highly recommend that any new projects using latest versions of .NET utilize FastEndpoints over MVC Controllers.