Design Patterns: Asp.Net Core Web API, services, and repositories

Part 3: Models, Controllers and the ClansController

Posted by Carl-Hugo Marcotte on August 17, 2017
Design Patterns: Asp.Net Core Web API, services, and repositories

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.

Skip the shared part

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:

  1. The web part; the HTTP request and response handling.
  2. The business logic; the domain.
  3. 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

Article Source code
Part 1: Introduction 1. NinjaApi - Starting point
Part 2: Dependency Injection DependencyInjection sample
Part 3: Models and Controllers 3. NinjaApi - ClansControllers
Part 4: Services and the ClanService 4. NinjaApi - The ClanService
Part 5: Repositories, the ClanRepository, and integration testing 5. NinjaApi - Clans completed
Part 6: the NinjaController and the ninja sub-system 6. NinjaApi - NinjaController
Part 7: the NinjaService 7. NinjaApi - NinjaService
Part 8: Azure table storage and the data model 8. NinjaApi - NinjaEntity
Part 9: the NinjaMappingService and the Façade pattern 9. NinjaApi - NinjaMappingService
Part 10: the NinjaRepository and ForEvolve.Azure 10. NinjaApi - NinjaRepository
Part 11: Integration testing 11. NinjaApi - IntegrationTesting
More might come someday…  

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:

  1. A controller handles an (HTTP) request
  2. Use a model
  3. 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:

  1. We have ninjas
  2. And clans
  3. A ninja can be a member of a clan.
  4. 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’s Name and the ninja’s Count.
  • A ClanDetails class that could be composed of the clan’s Name and a collection of ninja. Note that we could also include the Count.

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 :wink:.

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… :wink:


For more information about unit and integration testing see:


We will write our tests using XUnit.

I created a snippet that help code empty failing AAA tests faster (Arrange, Act, Assert). To use it, once installed, it is as easy as positioning your cursor in the body of a method, then typing a + tab + tab.
[Fact]
public void SomeTestMethod()
{
    // Arrange


    // Act


    // Assert
    throw new NotImplementedException();
}
The result of the 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:

  1. Expecting the ClansController.ReadAllAsync(); method to return an OkObjectResult.
  2. 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

Article Source code
Part 1: Introduction 1. NinjaApi - Starting point
Part 2: Dependency Injection DependencyInjection sample
Part 3: Models and Controllers 3. NinjaApi - ClansControllers
Part 4: Services and the ClanService 4. NinjaApi - The ClanService
Part 5: Repositories, the ClanRepository, and integration testing 5. NinjaApi - Clans completed
Part 6: the NinjaController and the ninja sub-system 6. NinjaApi - NinjaController
Part 7: the NinjaService 7. NinjaApi - NinjaService
Part 8: Azure table storage and the data model 8. NinjaApi - NinjaEntity
Part 9: the NinjaMappingService and the Façade pattern 9. NinjaApi - NinjaMappingService
Part 10: the NinjaRepository and ForEvolve.Azure 10. NinjaApi - NinjaRepository
Part 11: Integration testing 11. NinjaApi - IntegrationTesting
More might come someday…  

Resources

Some additional resources used during the article (or not).

Articles & concepts

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.





Comments