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

Part 1: Introduction

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

In this article series, I’d like to go back a little (from my previous Microservices Aggregation article which was more advanced) and introduce some more basic design patterns. Those patterns help decouple the application flow and extract its responsibilities into separate classes.

The goal is to give each class a single unique responsibility, which will help us follow the Single responsibility principle. See the SOLID principles for more information.

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.


The patterns

We will follow the same concept that are behind the N-tier architecture but, without creating multiple assemblies. In our case, we don’t need that level of separation and depending on how the code is organized; it is not mandatory.

If you don’t know anything about N-tier architecture, don’t worry I will explain what you need to know during the series.

More on that, with the way Asp.Net Core handles references by default, we would have access to our data access code from the presentation layer anyway which would void the idea behind layering.

That said, there are always multiple ways of having things done in software engineering, suffice to know enough and to choose wisely. So keep learning!


Instead of dividing a software into layers, we can split the software into small independent projects where each project (assembly) owns all of the code required for its sub-domain (or Bounded Context). This way we encapsulate the code in “domain bubbles” instead of layers. We could see this as cutting our software vertically instead of horizontally.

More on this another time, let’s go back to our business!


The basic idea is the following:

  1. A user sends an HTTP request to the server.
  2. Asp.Net MVC route the request to a Controller action.
  3. The action’s responsibility is to handle the request and the response, nothing more.
  4. Since the domain logic is not of the controller responsibility, it must be delegated to another object. That object is the Service.
  5. While the Service owns the domain logic responsibility, it cannot take another one so it cannot handle the data access logic. It must delegate that to the Repository.
  6. The Repository, as already stated, is responsible for the data-access. Basically, it read/write data from/to a data-source.
    • In our case, the data-source will be an Azure Storage’s Table.

This may sound complicated, but don’t worry it is not, it is pretty simple once you get used to it. This is a modular (reusable) and testable (reliable) approach.

An HTTP request from the Controller to the data-source passing by the Service and the Repository.

This is a good default way of doing things as long as there is no special cases or restrictions.


The Domain

Now that we settled on the parts we want to talk about, we need a subject for our Web API. The subject is the domain. Since coding is fun, let’s use “Ninjas” as our domain. Ninjas are always fun to talk about, so, why not, right?

To redraw the previous schema using Ninjas, we would need the following classes:

  1. The NinjaController class (presentation layer).
  2. The NinjaService class (domain logic layer).
  3. The NinjaRepository class (data access layer).
An HTTP request from the Controller to the data source, including some implementation details (hard coupling).

Dependency injection

As you may have noticed, a class that calls a second class that calls a third class means coupling.

  • NinjaController and NinjaService are directly coupled.
  • NinjaService and NinjaRepository are directly coupled.
  • NinjaController and NinjaRepository are indirectly coupled.

In our final application, we will break that coupling using Interfaces and we will bind everything together with Dependency Injection.

Conceptually, we will add the two following interfaces:

  1. The INinjaService interface (domain logic abstraction).
  2. The INinjaRepository interface (data access abstraction).

And we will implement them in these classes:

  1. The NinjaService class implements INinjaService.
  2. The NinjaRepository class implements INinjaRepository.

The conceptual representation of the process, at this point, could look like this:

An HTTP request from the Controller to the data source. This is a conceptual break of the hard coupling of the previous schema.

Since an interface is only an empty contract containing no code, a class that depends on an interface, only depends on its contract, not on any of its implementation. Applying this principle will allow us to decouple our classes.

From now on, our classes will only use other interfaces; never another concrete implementation. This could be represented as follow:

An HTTP request from the Controller to the data source, fully decoupled.

As we can see in the previous schema, the NinjaController class only know about the INinjaService interface and know nothing else. The same goes with the NinjaService class, its awareness of the system is limited to INinjaRepository interface. Even better, the NinjaRepository class is the only one that knows about Azure Storage.

By using interfaces instead of concrete classes, we broke the direct dependencies between our implementations.

As stated before, we will use Dependency Injection to link all of these back together. We will cover the basics of Dependency Injection in the next article of the series.


Interesting fact

Dependency Injection is now built-in Asp.Net Core; which is a really good thing.


The benefits

Once you get used to these patterns, it adds flexibility at near zero cost. The time to code a Service or a Repository is minimal, especially when using a tools like ForEvolve.Azure or a full-fledged ORM like Entity Framework.

Examples of what you could do

It gives you the opportunity to swap a data source with minimal to no impact on the domain logic. The swap can even be done at runtime.

Same thing goes for the Controller and the Service, helping you to keep your public API healthy.

For example, we could code two classes that implements INinjaRepository. We could then swap our data access logic from Azure Table Storage to MS SQL Server as easily as changing the dependency injection binding. Both implementation could be use at the same time for different purpose in our program, sharing the Domain Logic while persisting the data in different places.

Concrete idea: a configurable data source for each user, at runtime.

Multiple implementation of INinjaRepository allowing to easily swap data-sources without impacting the domain logic.

Swappable implementations example

To simplify things up, I implemented a small example using simplified classes. If you are interested in this topic, I invite you to take a look at my little demo project, available at GitHub.

In that project, I use the patterns explained in this series, so you might want to consider going through the series first.

The end of this article

What have we covered in this article?

We now understand or have learned a few concepts that we will apply in the articles of the series and the reasons behind these architectural choices. In each subsequent articles, we will focus a little more on each of those parts individually.

Here are the design patterns we talked about:

  • Controller (presentation layer) - MVC - Web API
  • Service (domain logic)
  • Repository (data access)
  • Dependency Injection (binding everything together using Inversion of Control)

What’s next?

Next, we will start writing code and see all that theory in practice, beginning by the Controller and the API Contracts. But before that, we will talk a little about Dependency Injection to help beginners understand a little more about this unavoidable subject.

If you are familiar with Dependency Injection in Asp.Net Core, feel free to skip that article.


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