Domain Driven Design with Web API revisited Part 16: the Web API consumer layer and the GET controller action

Introduction

In the previous post we built the concrete application service layer for our demo project. We saw that it was quite straightforward to implement the abstract service we inserted in the post before that.

It is time to build the top layer of the solution which will communicate with the application service. This post will be quite technical as we need to add an IoC container to the Web API project.

Web API layer

We’ll add a new Web API 2 project called WebSuiteDDD.WebApi to our solution. If you have VS 2013 then you can add a new ASP.NET project and select the Web API template:

Create asp.net project in visual studio 2013

Create new Web Api project in Visual Studio

If you’re on VS 2012 then you can install the Web API 2 template using this download link. It will install the MVC 5 templates for VS 2012 using the Web Installer. The new template will be available as follows:

Add new Web Api 2 project in visual studio 2012

I’m building this demo in Visual Studio 2012 but you should be able to follow the instructions if you’re using VS 2013 as well. The empty Web API 2 template in VS 2012 gave me the following skeleton project:

Empty Web API 2 project added into visual studio 2012

There will be a file called WebApiConfig in the App_Start folder with the following content:

public static class WebApiConfig
{
	public static void Register(HttpConfiguration config)
	{
		// Web API configuration and services

		// Web API routes
		config.MapHttpAttributeRoutes();

		config.Routes.MapHttpRoute(
			name: "DefaultApi",
			routeTemplate: "api/{controller}/{id}",
			defaults: new { id = RouteParameter.Optional }
		);
	}
}

Change the route template to…

routeTemplate: "{controller}/{id}"

…i.e. we don’t want to bother with the “api” bit in the URLs. If you’ve created a Web API project in Visual Studio 2013 then you’ll have a file called RouteConfig in App_Start. Open that file and remove the following bit of code:¨

routes.MapRoute(
	name: "Default",
	url: "{controller}/{action}/{id}",
	defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

For some reason the Web API template adds MVC related things as well, such as the one above. You’ll also see JavaScript files, CSS files etc., you can delete them all if you wish.

Right-click the Web API project and select “Set as StartUp Project”. This is to change the start up project role from the DemoDatabaseTester project we created earlier. Add a project reference to the WebSuiteDemo.Loadtesting.ApplicationServices, WebSuiteDemo.Loadtesting.Domain and WebSuiteDemo.Loadtesting.Repository.EF projects. We said before that the ultimate consumer layer shouldn’t depend on the concrete repository, so why do have this last dependency? Don’t worry, it’s there only for the IoC container we’ll install later on, it has nothing to do with a “real” business dependency.

Right-click the Controllers folder and click Add, Controller… Select the Web API 2 Controller – Empty template in the Add Scaffold dialog. Set the controller name to LoadtestsController. As promised you’ll get an empty controller which derives from the ApiController base class.

Our first action: GET

Our first task is to add a controller that will retrieve the load tests from the database and return the list in a readable format. We’ll assume that people will want to look at load tests 2 weeks ahead, i.e. we won’t bother with specifying the date in the query string right now. Also, we’ll make sure that our controller actions are all asynchronous, i.e. they will all return Task objects just like the methods in the application service.

We know that the API can load the data using the application service layer. Therefore the load test controller will need a dependency of type ITimetableService. We’ll provide it through the controller constructor so that we don’t have a tight coupling to any concrete ITimetableService implementation. If you don’t know what all this means and why it is good practice then you’ll need to go through the basics of the Dependency Inversion Principle available here and here.

Here’s the code for the GET method:

public class LoadtestsController : ApiController
{
	private readonly ITimetableService _timetableService;

	public LoadtestsController(ITimetableService timetableService)
	{
		if (timetableService == null) throw new ArgumentNullException("ITimetableService");
		_timetableService = timetableService;
	}

	public async Task<IHttpActionResult> Get()
	{
		GetLoadtestsForTimePeriodRequest getLoadtestsForTimePeriodRequest =
			new GetLoadtestsForTimePeriodRequest(DateTime.UtcNow, DateTime.UtcNow.AddDays(14));
		GetLoadtestsForTimePeriodResponse getLoadtestsForTimePeriodResponse =
			await _timetableService.GetLoadtestsForTimePeriodAsync(getLoadtestsForTimePeriodRequest);
		if (getLoadtestsForTimePeriodResponse.Exception == null)
		{
			return Ok<IEnumerable<LoadtestViewModel>>(getLoadtestsForTimePeriodResponse.Loadtests);
		}

		return InternalServerError(getLoadtestsForTimePeriodResponse.Exception);
	}
}

As you see we simply delegate the task to the ITimetableService dependency. We then return either an OK with the load test view models or an internal server error with the exception details.

Dependency injection in controllers

We let the consumer of LoadtestsController provide an implementation of ITimetableService. The consumer of the controller should provide an implementation of ITimetableService. However, where do we provide that implementation? Where is the caller to CustomersController? It probably lies hidden within .NET and its MVC routing logic.

Before we can test our first controller action we need to sort out some IoC infrastructure.

IoC containers and StructureMap

Fortunately for us this is not a new problem and has been solved using Inversion of Control (IoC) containers. IoC containers are typically quite complex mechanisms that can “magically” construct concrete implementation of abstract dependencies on the fly. There are many IoC containers out there for .NET and one of the most popular ones is StructureMap.

StructureMap comes in many flavours. If you open the NuGet package manager and search for “structuremap” you’ll get a lot of different results. We’ll need the one specifically for Web API 2:

StructureMap installation from NuGet for Web API 2

It will add several libraries and new files to the project:

StructureMap related files added to project

These are all auto-generated files. In fact the installer also added MVC-related classes, such as StructuremapMvc.cs. We won’t need all of those. You’ll probably get compiler errors since StructureMap added some MVC-related classes and we only have dlls for the Web API. We won’t install the MVC ones just to have the code working. You can safely delete StructureMapMvc.cs from App_Start and StructureMapScopeModule class from the DependencyResolution folder as well.

Open StructuremapWebApi.cs in App_Start. We need to modify the Start method as it still depends on MVC:

public static class StructuremapWebApi
{
	public static void Start()
	{
		IContainer container = IoC.Initialize();	
		container.AssertConfigurationIsValid();
                Debug.WriteLine(container.WhatDoIHave());  
		GlobalConfiguration.Configuration.DependencyResolver = new StructureMapWebApiDependencyResolver(container);
	}
}

The AssertConfigurationIsValid method will throw a descriptive exception message in case StructureMap cannot resolve a certain dependency. The WhatDoIHave() method will return the resolved types. We’ll take a look at the output further on.

Next, locate IoC.cs whose Initialize method is called upon by StructuremapWebApi.cs. You’ll see that it’s short and it only registers a registry called DefaultRegistry:

public static IContainer Initialize()
{
	return new Container(c => c.AddRegistry<DefaultRegistry>());
}

OK, let’s open DefaultRegistry.cs in the DependencyResolution folder.

The default convention for interface abstractions is that if StructureMap sees an interface whose name starts with an “I” then it will look for an implementation with the same name without the “I”: ICustomerService – CustomerServicer, ICustomerRepository – CustomerRepository.

It’s here in DefaultRegistry.cs that you can declare your concrete types for the abstract dependencies, e.g.:

public DefaultRegistry()
		{
			Scan(
				scan =>
				{
					scan.TheCallingAssembly();
					scan.WithDefaultConventions();
				});
			For<ICustomerRepository>().Use<BrilliantCustomerRepository>();
		}

Note the usage of For and Use in the DefaultRegistry constructor. The WithDefaultConventions reflects the “I” rule for interfaces outlined above.

There’s something missing still. StructureMap will scan the calling assembly for matching dependencies as declared by the “TheCallingAssembly()” method call. We have to tell it to look elsewhere in the project. The above code won’t be enough for StructureMap to find the ITimetableService implementation which is TimetableService and is located in another assembly. There’s a solution for that of course. We can tell StructureMap to look for dependencies in other assemblies, specifically in assemblies that contain a certain type. We know that our concrete implementations are located in the application service and concrete repository layers so we can select two types from those and tell StructureMap to go and look there as well. Here’s the updated “scan” block:

Scan(
	scan =>
	{
		scan.TheCallingAssembly();
		scan.AssemblyContainingType<TimetableService>();
		scan.AssemblyContainingType<TimetableViewModelRepository>();
		scan.WithDefaultConventions();
	});

Make sure you understand the AssemblyContainingType method. It simply says that please StructureMap, look for concrete implementations of abstractions in the assemblies that contain the class TimetableService and TimetableViewModelRepository. We could have taken any other unique class name from those assemblies. TimetableService is located in WebSuiteDemo.Loadtesting.ApplicationServices and TimetableViewModelRepository is located in the WebSuiteDemo.Loadtesting.Repository.EF assembly. Therefore StructureMap should find those concrete classes we need in the Web API and application service projects since we followed the default “I” interface naming convention.

This should be enough for the IoC section.

Connection strings in the config file

We have to add the same connection string to the Web API project as we added to the database tester C# console application. Open Web.config of the Web API project and add the following section below the closing “appSettings” node:

<connectionStrings>
	<add name="WebSuiteContext" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDB)\v11.0;Initial Catalog=WebSuiteDDD.Repository.EF.WebSuiteContext;Integrated Security=True;MultipleActiveResultSets=True" />
</connectionStrings>

OK, we’re done with the setup phase.

Testing the GET method

Start the application. A web browser page should open with an exception message saying that “The Web server is configured to not list the contents of this directory.”. That’s fine, we have to navigate to the /loadtests endpoint. Add a breakpoint to the Get method in LoadtestsController.cs. In the browser add the /loadtests bit to the URL so that it reads “http://localhost:xxxxx/loadtests&#8221; and press Enter. The routing engine will find our Get method and code execution will stop.

Before we continue let’s view the Debug window where StructureMap should output its “findings”. You’ll see a couple of dependency resolutions but the ones that are most interesting for us are the following:

ITimetableRepository
ITimetableService
ITimetableViewModelRepository

Further to the right in the output you’ll see which concrete implementations StructureMap has found:

WebSuiteDemo.Loadtesting.Repository.EF.TimetableRepository
WebSuiteDemo.Loadtesting.ApplicationServices.Implementations.TimetableService
WebSuiteDemo.Loadtesting.Repository.EF.Repositories.TimetableViewModelRepository

You can go through the code step by step with F5. You’ll see that all dependencies have been resolved correctly. I encourage you to execute the code in this manner and see how the bits and pieces are linked instead of me writing a litany here. It should be quite simple a straightforward to follow.

At the time of testing this code I had 3 load test records in the local database. I tested the Get method in Chrome and it presented the data in XML format. Here’s an example:

<LoadtestViewModel>
   <AgentCity>Seattle</AgentCity>
   <AgentCountry>USA</AgentCountry>
   <CustomerName>Great customer</CustomerName>
   <DurationSec>600</DurationSec>
   <EngineerName>Jane</EngineerName>
   <Id>0220dd95-53b3-4fc8-a803-3c7ee4081c2c</Id>
   <LoadtestTypeShortDescription>Capacity test</LoadtestTypeShortDescription>
   <ProjectName>Second project</ProjectName>
   <ScenarioUriOne>www.greatsite.com</ScenarioUriOne>
   <ScenarioUriThree>www.neverheardofsite.com</ScenarioUriThree>
   <ScenarioUriTwo>www.nosuchsite.com</ScenarioUriTwo>
   <StartDateUtc>2015-08-30T21:48:52.323</StartDateUtc>
   <UserCount>120</UserCount>
</LoadtestViewModel>

If you get no data records at all you can add some manually directly in the database when viewing the contents of the Loadtests table:

Adding records manually to the loadtests table

Alternatively change the start dates of the existing load test records, that’s equally fine and much quicker.

We’ll stop here and continue with the POST and DELETE action methods in the next post.

View the list of posts on Architecture and Patterns here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

One Response to Domain Driven Design with Web API revisited Part 16: the Web API consumer layer and the GET controller action

  1. Martin says:

    >>We said before that the ultimate consumer layer shouldn’t depend on the concrete repository, so why do have this last dependency?<<

    Therefore, it's mostly always a good idea to separate interfaces from implementation in a separate dll, isn't it?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: