Design Patterns: Asp.Net Core Web API, services, and repositories
Part 3: Models, Controllers and the ClansController

In the first article, we visited a few concepts that we will apply, starting in the current article.
The second article was an overview of Dependency Injection.
In this 3rd article, we will:
- Model the ninja
- Create the
ClansController
(ninja’s don’t fight alone!) - Write some tests
All of this will get us ready for the next article’s ClanService
.
The series (shared section)
In the series, we will create an Asp.Net Core 2.0 Web API, and we will focus on the following major concerns:
- The web part; the HTTP request and response handling.
- The business logic; the domain.
- The data access logic; reading and writing data.
During the article, I will try to include the thinking process behind the code.
Technology-wise, we will use Asp.Net Core, Azure Table Storage and ForEvolve Framework to build the Web API.
To use the ForEvolve Framework (or let’s say toolbox), you will need to install packages from a custom NuGet feed.
If you dont know How to use a custom NuGet feed in Visual Studio 2017, feel free to take a look at this article.
If you do, the ForEvolve NuGet feed URI is https://www.myget.org/F/forevolve/api/v3/index.json
.
We will also use XUnit and Moq for both unit and integration testing.
Table of content
I will update the table of content as the series progress.
“Prerequisites”
In the series, I will cover multiple subjects, more or less in details, and I will assume that you have a little idea about what a Web API is, that you know C# and that you already have a development environment setup (i.e.: Visual Studio, Asp.Net Core, etc.).
The goal
At the end of this article series, you should be able to program an Asp.Net Core Web API in a structured and testable way using the explained techniques (design patterns). These design patterns offer a clean way to follow the Single Responsibility Principle.
Since design patterns are language-agnostic, you can use them in different applications and languages. In an Angular application, you will most likely use Dependency Injection for example.
This is one of the beauties of design patterns; they are tools to be used, not feared!
Asp.Net Core 2.0
At the time of the writing, Asp.Net Core 2.0 was still in prerelease, and I updated the code samples to use the release version.
You will need the .NET Core 2.0.0 SDK and Visual Studio 2017 update 3 or the IDE/code editor of your choosing.
Asp.Net Core MVC
In the past, MVC and WebApi were two separate things. Now with Asp.Net Core, they are unified, woot! Even more, Razor Pages were also unified in ASP.NET Core 2.0.0.
MVC pattern summary
MVC stands for Model-View-Controller. The pattern could be shortened to:
- A controller handles an (HTTP) request
- Use a model
- Then render a view.
The use of MVC help split responsibilities.
Since we are building a Web API, we will not render views but serialize objects instead. I like to name those objects the “Web API Contracts.”
Web API Contracts
In the MVC acronym, the API Contracts could be seen as the “MV” part: the model and the view. Since there is no View in a Web API, these objects can be seen as playing the View role (and they are models).
More on that, the Web API Contracts are the public objects that are transferred over HTTP. We could also call them Data Transfer Object (DTO), but I prefer “Contracts” since they are not to be messed with once deployed. They are more than simple “object to be transferred” they are what our clients rely on in production.
The “data contracts” play a big role in the Web API interface so: they cannot change, they can only evolve.
Side note
It is possible to have multiple models in an application: view models, domain models, data models, etc. Most of the time, I would recommend the use of multiple models, its helps separate and isolate responsibilities.
Even in our little Ninja App, we will have 2 model layers: the “presentation & domain model” and the “data model.”
A great tool to manage copying models (objects) from one layer to another is AutoMapper.
The Ninja Model
The Ninja App model is super simple:
- We have ninjas
- And clans
- A ninja can be a member of a clan.
- A clan can be composed of many ninjas.
Here are the C# classes:
namespace ForEvolve.Blog.NinjaApi.Models
{
public class Ninja
{
public string Key { get; set; }
public Clan Clan { get; set; }
public string Name { get; set; }
public int Level { get; set; }
}
public class Clan
{
public string Name { get; set; }
}
}
As you may have noticed by looking at the Clan
class, there is no “a clan can be composed of many ninjas” represented in code.
The reason behind this is simple: I want to keep the model super lean.
Conceptually speaking, we could recreate that condition manually by requesting all ninja of a specific clan.
That said, if I want to transfer more than those simple objects, I could create a Gateway (see below) exposing different endpoints.
For example:
- A
ClanSummary
class that could be composed of the clan’sName
and the ninja’sCount
.- A
ClanDetails
class that could be composed of the clan’sName
and a collection of ninja. Note that we could also include theCount
.
In the little Ninja App that we are building, the goal is to create raw data endpoints that read/write simple entities, that’s it.
So we will skip the more fancy models for now .
The Controllers
Now that we have our model, let’s focus on the controllers.
The Controller
responsibility is to handle the HTTP requests and responses.
Our ultra simple Ninja Web API will simply read or write Clans and Ninja’s information.
To do so, we will create two controllers, the ClansController
and the NinjaController
.
Each controller will be responsible for its own model part: Clan
for the ClansController
and Ninja
for the NinjaController
.
Side note
A good way to design a system is to start here, at the “user interface” level, which in the case of a Web API, is the
Controller
. We first need to define the data contracts (the Web API interface) that a client would need to efficiently (or easily) use our system. Then map the supported operations to an HTTP verb (GET
to read,POST
to create, etc.).If we were building one or more GUI, we should start there (that’s the user interface level, right?). Begining by the analysis of what data would be needed for each GUI. Then maybe create one or more API Gateway.
Raw data endpoints are what we will create in our Ninja App.
ClansController
The ClansController
only expose a read all clans endpoint. The clan’s data will be static and hard coded but could be persisted in a database at a later time.
You could do this refactoring at the end of the series to practice your new skills.
namespace ForEvolve.Blog.Samples.NinjaApi.Controllers
{
[Route("v1/[controller]")]
public class ClansController : Controller
{
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<Clan>), 200)]
public Task<IActionResult> ReadAllAsync()
{
throw new NotImplementedException();
}
}
}
Unit Tests
During the article series, instead of just throwing some code out, I will write code, write tests, update the code and maintain the test suite.
As for the code conventions, I like to follow the ASP.NET Engineering guidelines - Unit tests and functional tests.
I also like to create a class for the subject under test and then a sub-class for each method.
I like how easy it becomes to regroup all the tests for one method under its own sub-class as well as to create mocks only once per class.
This will be clearer and more obvious later, in the NinjaControllerTest
class.
Unit testing
It is a good practice to create automated tests for your code. This help reach a higher level of quality, and a greater degree of confidence when comes the time to deploy your application.
I also have to announce to the TDD purists that I hate to see all red-wavy-underlined-code with errors saying that my interfaces or classes does not exist. So I will start by defining my interfaces then I will write tests.
Let’s say that I am more an “interface-first”/”interface-driven-testing” kinda guy…
For more information about unit and integration testing see:
We will write our tests using XUnit.
positioning your cursor in the body of a method
, then typing a
+ tab
+ tab
.
[Fact]
public void SomeTestMethod()
{
// Arrange
// Act
// Assert
throw new NotImplementedException();
}
aaa
snippet available at GitHub
Ok, now it is time to write some failing tests for our clan controller.
ClansControllerTest
The only test of the clan is the ReadAllAsync.Should_return_OkObjectResult_with_clans()
test.
In this test we are:
- Expecting the
ClansController.ReadAllAsync();
method to return anOkObjectResult
. - Expecting the result value to be the
expectedClans
array (this obviously cannot happen yet but will fix this later).
namespace ForEvolve.Blog.Samples.NinjaApi.Controllers
{
public class ClansControllerTest
{
protected ClansController ControllerUnderTest { get; }
public ClansControllerTest()
{
ControllerUnderTest = new ClansController();
}
public class ReadAllAsync : ClansControllerTest
{
[Fact]
public async void Should_return_OkObjectResult_with_clans()
{
// Arrange
var expectedClans = new Clan[]
{
new Clan { Name = "Test clan 1" },
new Clan { Name = "Test clan 2" },
new Clan { Name = "Test clan 3" }
};
// Act
var result = await ControllerUnderTest.ReadAllAsync();
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
Assert.Same(expectedClans, okResult.Value);
}
}
}
}
The end of this article
At this point, if we hit the “Run All” button of the Visual Studio “Test Explorer” window, we should see 1 failing test.
This may not sound that good, but this is our refactoring starting point. Now we only need to write enough code to make that test pass.
What have we covered in this article?
We talked about the controller’s role, about models and data contracts. We also coded a unit test to improve the quality of our Ninja App.
What’s next?
In the next article, we will create the IClanService
interface and its default implementation, the ClanService
class.
A new class means writing more unit tests; as well as updating the ClansController
to use that new interface.
Our focus will gravitate around the “service” role (the business logic; a.k.a. the domain) and how to connect those two units (the service and the controller).
Until then, happy coding!
Last word (shared section)
Table of content
Resources
Some additional resources used during the article (or not).
Articles & concepts
- Bounded Context
- Dependency Injection
- From STUPID to SOLID Code!
- Mocks Aren’t Stubs
- Testing and debugging ASP.NET Core
Tools & technologies
Code samples
Special thanks
I’d like to finish with special thanks to Emmanuel Genest who took the time to read my drafts and give me comments from a reader point of view.