Replacing the Service Layer with an HTTP API : 10 Years of .Net Compressed into Weeks #13
on
This post looks at choosing a service layer for a web application. This post is part of a blog series ASP.Net 10 Years On. Even though this is part of a series I have tried to make each post standalone. In the previous post we wrapped up the data access layer and look to move onto the API layer.
9 out of 10 architectures I have worked with have had service layer projects with classes called SomethingService e.g. ProductService that contained methods such as GetProducts, GetProductByID etc.
Such methods can be easily be exposed for an API by converting the methods into messages such as SOAP or using .Net features such as Remoting.
In the architecture for our SMS Quiz we started off with a service layer that would serve a double purpose of firstly powering a REST API and a Website layer (MVC project). The idea was to avoid duplicate code between the Web and API layer.
Here's a diagram of how it looked (the application layer is the service layer):
Before we look to create an API layer for the quiz application to expose the functionality provided by the service layer let's consider the number of layers we have and how (potentially) similar they are.
As I began to flesh out the code and unit tests for the API and Web layers I noticed a lot of similarities between the two. The controllers were very similar and each project had almost identical sets of unit tests.
Here's an example of the similar looking methods. All these methods are very short, since the service layer is doing the real work.
An example method from an MVC controller:
public ActionResult Index()
{
CompetitionViewModel viewModel = new CompetitionViewModel();
viewModel.Competitions = _competitionService.GetCompetitions().Competitions;
return View("Index", viewModel);
}
Now, an almost identical method in the Web API project:
public IEnumerable<CompetitionItem> Get()
{
return _competitionService.GetCompetitions().Competitions;
}
As well as the near duplicate methods there is also a bunch of duplicate configuration such as Ninject bindings for dependency injection, mapper configuration, logging etc that are required when creating instances of service layer classes. So, what's the solution? It's easy: delete stuff!
By making the Web API the service layer also we can remove all configuration elements from the MVC project, merge the service layer code into the API and delete the service layer completely. This means that we can use HTTP as the transport protocol and we can consume the same API we plan to expose externally, internally; otherwise known as eating your own dog food.
Here's how the new architecture looks:
The updated Web API controller methods now look more like the service layer methods i.e. they use the repository layer and mapping:
public IEnumerable<CompetitionItem> Get()
{
var competitions = _competitionRepository.Find("Status=@Status", new { Status = CompetitionStatus.Open });
return _mapper.Map<IEnumerable<Competition>, IEnumerable<CompetitionItem>>(competitions);
}
The updated MVC action methods now resemble something like:
public ActionResult Index()
{
var response = _client.GetAsync("competitions").Result;
if (response.IsSuccessStatusCode)
{
dynamic users = response.Content.ReadAsAsync<dynamic>().Result;
return View(users);
}
return ErrorView(response);
}
The beauty of this approach is that, other than a URL, the MVC layer requires no extra configuration. Adopting principles of REST with regard to statelessness combined with this approach can also make scaling out and load balancing a little easier.
Our web layer is now platform independent. Do we even need server side code for the web project? We can very easily use a client-side library like Backbone.js or Knockout.js without having to make any changes to the API.
As well has having an architecture that's easier to scale and requires less configuration I also got to delete a few projects from the solution making my project more streamlined yet more capable. Win!
NB future blog posts will take a deeper dive into the technical aspects of this approach.
The source code for this (work in progress) project can be found at: github.com/bbraithwaite/smsquiz.
Key code elements discussed in this post:
- HttpClient wrapper from MVC project to limit duplicate code.
- Ninject bindings for HTTPClient in MVC project.
- Controller using HttpClient via dependency injection.
In the next post we look at creating the Web API.