Design Patterns: Asp.Net Core Web API, services, and repositories
Part 9: the NinjaMappingService and the Façade pattern

In the previous article, we explored Azure Table Storage briefly, and we created the NinjaEntity
class.
Doing so opened up a new concern: mapping Ninja
to NinjaEntity
.
Before going further, to keep the external dependencies low, in this article, we will create a mapping system. This will also allow us to explore an additional design pattern: the Façade.
Tools
In a real life project, to speed up development, I would recommend the use of a library like AutoMapper. AutoMapper is a great tool that allows copying one object into another (and much more).
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.
NinjaMappingService
Before going further, we will create an INinjaMappingService
interface that will become our “ninja mapping hub.”
The INinjaMappingService
responsibility is to offer a centralized and convenient way to convert Ninja
to NinjaEntity
and vice versa.
We will also need to convert IEnumerable<NinjaEntity>
to IEnumerable<Ninja>
(for the ReadAll*()
methods).
In its current state, our Ninja App does not need to convert IEnumerable<Ninja>
to IEnumerable<NinjaEntity>
so we will omit that functionality to keep our project clean of useless code.
If the need of such operation ever arises, we will add it then, and only then.
I could have added those methods directly in the
NinjaRepository
class, but remember SOLID and it single responsibility principle (SRP): A class should have only one reason to change.
namespace ForEvolve.Blog.Samples.NinjaApi.Services
{
public interface INinjaMappingService
{
Ninja Map(NinjaEntity entity);
NinjaEntity Map(Ninja ninja);
IEnumerable<Ninja> Map(IEnumerable<NinjaEntity> entity);
}
}
While thinking about it, a little more, I want to use Ninja Map(NinjaEntity entity);
in IEnumerable<Ninja> Map(IEnumerable<NinjaEntity> entity);
.
This could be a little harder to test than expected, which leads me to a more fine-grained design.
We will keep the INinjaMappingService
, but it will simply become a Façade.
Mapping subsystem
Behind our Façade hides the mapping subsystem. I created a little visual representation of the design.
Beware, the schema looks complicated, but it is pretty simple. The hard part was to represent the concept in a single diagram.
I used colors to identify elements.
-
Ninja Map(NinjaEntity entity);
reference color is blue. -
NinjaEntity Map(Ninja ninja);
reference color is green. -
IEnumerable<Ninja> Map(IEnumerable<NinjaEntity> entity);
reference color is yellow.
Let’s take a look:
The first large red interface is a generic mapping interface with only one method defined: the Map
method.
namespace ForEvolve.Blog.Samples.NinjaApi.Mappers
{
public interface IMapper<TSource, TDestination>
{
TDestination Map(TSource entity);
}
}
In the second “row” there are six interfaces. Those interfaces do not exist in code; they are simply defining the generic types of IMapper<TSource, TDestination>
.
Even more, the first three are the same as the last three; I only needed more space to make the diagram clearer.
I would have needed a 3D diagram to make this clearer…
The left side
We will create three classes, each one with a single mapping responsibility (implementing IMapper<TSource, TDestination>
).
-
NinjaEntityToNinjaMapper
will implementIMapper<NinjaEntity, Ninja>
-
NinjaToNinjaEntityMapper
will implementIMapper<Ninja, NinjaEntity>
-
NinjaEntityEnumerableToNinjaMapper
will implementIMapper<IEnumerable<NinjaEntity>, IEnumerable<Ninja>>
This is a very flexible design where each mapper is independent.
NinjaToNinjaEntityMapper
NinjaToNinjaEntityMapper
implement IMapper<Ninja, NinjaEntity>
.
Its role is to convert Ninja
to NinjaEntity
.
The test:
namespace ForEvolve.Blog.Samples.NinjaApi.Mappers
{
public class NinjaToNinjaEntityMapperTest
{
protected NinjaToNinjaEntityMapper MapperUnderTest { get; }
public NinjaToNinjaEntityMapperTest()
{
MapperUnderTest = new NinjaToNinjaEntityMapper();
}
public class Map : NinjaToNinjaEntityMapperTest
{
[Fact]
public void Should_return_a_well_formatted_entity()
{
// Arrange
var ninja = new Ninja
{
Key = "Some key",
Name = "Some name",
Level = 45,
Clan = new Clan { Name = "Super clan" }
};
// Act
var result = MapperUnderTest.Map(ninja);
// Assert
Assert.Equal("Some key", result.RowKey);
Assert.Equal("Some name", result.Name);
Assert.Equal(45, result.Level);
Assert.Equal("Super clan", result.PartitionKey);
}
}
}
}
The implementation:
namespace ForEvolve.Blog.Samples.NinjaApi.Mappers
{
public class NinjaToNinjaEntityMapper : IMapper<Ninja, NinjaEntity>
{
public NinjaEntity Map(Ninja ninja)
{
var entity = new NinjaEntity
{
PartitionKey = ninja.Clan.Name,
RowKey = ninja.Key,
Name = ninja.Name,
Level = ninja.Level
};
return entity;
}
}
}
NinjaEntityToNinjaMapper
NinjaEntityToNinjaMapper
implement IMapper<NinjaEntity, Ninja>
.
Its role is to convert Ninja
to NinjaEntity
.
The test:
namespace ForEvolve.Blog.Samples.NinjaApi.Mappers
{
public class NinjaEntityToNinjaMapperTest
{
protected NinjaEntityToNinjaMapper MapperUnderTest { get; }
public NinjaEntityToNinjaMapperTest()
{
MapperUnderTest = new NinjaEntityToNinjaMapper();
}
public class Map : NinjaEntityToNinjaMapperTest
{
[Fact]
public void Should_return_a_well_formatted_ninja()
{
// Arrange
var entity = new NinjaEntity
{
Level = 10,
Name = "Some fake name",
PartitionKey = "Some clan name",
RowKey = "Some ninja key"
};
// Act
var result = MapperUnderTest.Map(entity);
// Assert
Assert.Equal(10, result.Level);
Assert.Equal("Some fake name", result.Name);
Assert.NotNull(result.Clan);
Assert.Equal("Some clan name", result.Clan.Name);
Assert.Equal("Some ninja key", result.Key);
}
}
}
}
The implementation:
namespace ForEvolve.Blog.Samples.NinjaApi.Mappers
{
public class NinjaEntityToNinjaMapper : IMapper<NinjaEntity, Ninja>
{
public Ninja Map(NinjaEntity entity)
{
var ninja = new Ninja
{
Key = entity.RowKey,
Clan = new Clan { Name = entity.PartitionKey },
Level = entity.Level,
Name = entity.Name
};
return ninja;
}
}
}
NinjaEntityEnumerableToNinjaMapper
NinjaEntityEnumerableToNinjaMapper
implement IMapper<IEnumerable<NinjaEntity>, IEnumerable<Ninja>>
.
Its role is to convert Ninja
to NinjaEntity
.
The test:
namespace ForEvolve.Blog.Samples.NinjaApi.Mappers
{
public class NinjaEntityEnumerableToNinjaMapperTest
{
protected NinjaEntityEnumerableToNinjaMapper MapperUnderTest { get; }
protected Mock<IMapper<NinjaEntity, Ninja>> NinjaEntityToNinjaMapperMock { get; }
public NinjaEntityEnumerableToNinjaMapperTest()
{
NinjaEntityToNinjaMapperMock = new Mock<IMapper<NinjaEntity, Ninja>>();
MapperUnderTest = new NinjaEntityEnumerableToNinjaMapper(NinjaEntityToNinjaMapperMock.Object);
}
public class Map : NinjaEntityEnumerableToNinjaMapperTest
{
[Fact]
public void Should_delegate_mapping_to_the_sinlge_entity_mapper()
{
// Arrange
var ninja1 = new NinjaEntity();
var ninja2 = new NinjaEntity();
var ninjaEntities = new List<NinjaEntity> { ninja1, ninja2 };
NinjaEntityToNinjaMapperMock
.Setup(x => x.Map(It.IsAny<NinjaEntity>()))
.Returns(new Ninja())
.Verifiable();
// Act
var result = MapperUnderTest.Map(ninjaEntities);
// Assert
NinjaEntityToNinjaMapperMock.Verify(x => x.Map(ninja1), Times.Once);
NinjaEntityToNinjaMapperMock.Verify(x => x.Map(ninja2), Times.Once);
}
}
}
}
The implementation:
namespace ForEvolve.Blog.Samples.NinjaApi.Mappers
{
public class NinjaEntityEnumerableToNinjaMapper : IMapper<IEnumerable<NinjaEntity>, IEnumerable<Ninja>>
{
private readonly IMapper<NinjaEntity, Ninja> _ninjaEntityToNinjaMapper;
public NinjaEntityEnumerableToNinjaMapper(IMapper<NinjaEntity, Ninja> ninjaEntityToNinjaMapper)
{
_ninjaEntityToNinjaMapper = ninjaEntityToNinjaMapper ?? throw new ArgumentNullException(nameof(ninjaEntityToNinjaMapper));
}
public IEnumerable<Ninja> Map(IEnumerable<NinjaEntity> entities)
{
var count = entities.Count();
var all = new Ninja[count];
for (int i = 0; i < count; i++)
{
var entity = entities.ElementAt(i);
var ninja = _ninjaEntityToNinjaMapper.Map(entity);
all[i] = ninja;
}
return all;
}
}
}
The right side
As you can maybe deduce from IMapper<TSource, TDestination>
and the INinjaMappingService
definition (and the diagram), INinjaMappingService
can simply inherit from IMapper<TSource, TDestination>
with three different generic pairs.
The updated INinjaMappingService
interface now looks like this:
namespace ForEvolve.Blog.Samples.NinjaApi.Services
{
public interface INinjaMappingService : IMapper<Ninja, NinjaEntity>, IMapper<NinjaEntity, Ninja>, IMapper<IEnumerable<NinjaEntity>, IEnumerable<Ninja>>
{
}
}
The tricky part of the diagram was to illustrate the NinjaMappingService
class relations.
In words: NinjaMappingService
implement INinjaMappingService
and indirectly uses the three mappers.
This makes NinjaMappingService
coupled only with the IMapper
interface, which keeps our application loosely coupled.
The full description goes as follow:
-
NinjaMappingService
implementINinjaMappingService
. -
NinjaMappingService
use anIMapper<NinjaEntity, Ninja>
. The runtime implementation will beNinjaEntityToNinjaMapper
. -
NinjaMappingService
use anIMapper<Ninja, NinjaEntity>
. The runtime implementation will beNinjaToNinjaEntityMapper
. -
NinjaMappingService
use aIMapper<IEnumerable<NinjaEntity>, IEnumerable<Ninja>>
. The runtime implementation will beNinjaEntityEnumerableToNinjaMapper
.
This is the Façade
I was talking about earlier. It gives access to the ninja mapping subsystem in a convenient and centralized way.
We could hide the individual mappers, we could create a mapping assembly, we could create a mapper factory, or we could have simply used AutoMapper
.
The full NinjaMappingService
looks like this:
namespace ForEvolve.Blog.Samples.NinjaApi.Services
{
public class NinjaMappingService : INinjaMappingService
{
private readonly IMapper<Ninja, NinjaEntity> _ninjaToNinjaEntityMapper;
private readonly IMapper<NinjaEntity, Ninja> _ninjaEntityToNinjaMapper;
private readonly IMapper<IEnumerable<NinjaEntity>, IEnumerable<Ninja>> _ninjaEntityEnumerableToNinjaMapper;
public NinjaMappingService(
IMapper<Ninja, NinjaEntity> ninjaToNinjaEntityMapper,
IMapper<NinjaEntity, Ninja> ninjaEntityToNinjaMapper,
IMapper<IEnumerable<NinjaEntity>, IEnumerable<Ninja>> ninjaEntityEnumerableToNinjaMapper
)
{
_ninjaToNinjaEntityMapper = ninjaToNinjaEntityMapper ?? throw new ArgumentNullException(nameof(ninjaToNinjaEntityMapper));
_ninjaEntityToNinjaMapper = ninjaEntityToNinjaMapper ?? throw new ArgumentNullException(nameof(ninjaEntityToNinjaMapper));
_ninjaEntityEnumerableToNinjaMapper = ninjaEntityEnumerableToNinjaMapper ?? throw new ArgumentNullException(nameof(ninjaEntityEnumerableToNinjaMapper));
}
public NinjaEntity Map(Ninja entity)
{
return _ninjaToNinjaEntityMapper.Map(entity);
}
public Ninja Map(NinjaEntity entity)
{
return _ninjaEntityToNinjaMapper.Map(entity);
}
public IEnumerable<Ninja> Map(IEnumerable<NinjaEntity> entities)
{
return _ninjaEntityEnumerableToNinjaMapper.Map(entities);
}
}
}
Once again, pretty simple code: easy to read, test and reuse.
NinjaMappingService unit tests
The NinjaMappingServiceTest
class code looks like:
namespace ForEvolve.Blog.Samples.NinjaApi.Services
{
public class NinjaMappingServiceTest
{
protected NinjaMappingService ServiceUnderTest { get; }
protected Mock<IMapper<Ninja, NinjaEntity>> NinjaToNinjaEntityMapperMock { get; }
protected Mock<IMapper<NinjaEntity, Ninja>> NinjaEntityToNinjaMapperMock { get; }
protected Mock<IMapper<IEnumerable<NinjaEntity>, IEnumerable<Ninja>>> NinjaEntityEnumerableToNinjaMapperMock { get; }
public NinjaMappingServiceTest()
{
NinjaToNinjaEntityMapperMock = new Mock<IMapper<Ninja, NinjaEntity>>();
NinjaEntityToNinjaMapperMock = new Mock<IMapper<NinjaEntity, Ninja>>();
NinjaEntityEnumerableToNinjaMapperMock = new Mock<IMapper<IEnumerable<NinjaEntity>, IEnumerable<Ninja>>>();
ServiceUnderTest = new NinjaMappingService(
NinjaToNinjaEntityMapperMock.Object,
NinjaEntityToNinjaMapperMock.Object,
NinjaEntityEnumerableToNinjaMapperMock.Object
);
}
[Fact]
public void Map_Ninja_to_NinjaEntity_should_delegate_to_NinjaToNinjaEntityMapper()
{
// Arrange
var ninja = new Ninja();
var expectedEntity = new NinjaEntity();
NinjaToNinjaEntityMapperMock
.Setup(x => x.Map(ninja))
.Returns(expectedEntity);
// Act
var result = ServiceUnderTest.Map(ninja);
// Assert
Assert.Same(expectedEntity, result);
}
[Fact]
public void Map_NinjaEntity_to_Ninja_should_delegate_to_NinjaEntityToNinjaMapper()
{
// Arrange
var ninjaEntity = new NinjaEntity();
var expectedNinja = new Ninja();
NinjaEntityToNinjaMapperMock
.Setup(x => x.Map(ninjaEntity))
.Returns(expectedNinja);
// Act
var result = ServiceUnderTest.Map(ninjaEntity);
// Assert
Assert.Same(expectedNinja, result);
}
[Fact]
public void Map_NinjaEntityEnumerable_to_NinjaEnumerable_should_delegate_to_NinjaEntityEnumerableToNinjaMapper()
{
// Arrange
var ninjaEntities = new List<NinjaEntity>();
var expectedNinja = new List<Ninja>();
NinjaEntityEnumerableToNinjaMapperMock
.Setup(x => x.Map(ninjaEntities))
.Returns(expectedNinja);
// Act
var result = ServiceUnderTest.Map(ninjaEntities);
// Assert
Assert.Same(expectedNinja, result);
}
}
}
Once again, due to the subsystem design, our tests are more than simple! Note that I am not testing the mapping here but the Façade. Each mapper has also been tested individually.
Feel free to post any questions that you may have in the comments.
Refactoring NinjaEntityEnumerableToNinjaMapper
If we take a look at NinjaEntityEnumerableToNinjaMapper
, we could easily create a more generalized implementation that would support any collection.
I will first rename NinjaEntityEnumerableToNinjaMapper
to EnumerableMapper
, which makes more sense.
Here is the code, I will explain it after:
namespace ForEvolve.Blog.Samples.NinjaApi.Mappers
{
public class EnumerableMapper<TSource, TDestination> : IMapper<IEnumerable<TSource>, IEnumerable<TDestination>>
{
private readonly IMapper<TSource, TDestination> _singleMapper;
public EnumerableMapper(IMapper<TSource, TDestination> singleMapper)
{
_singleMapper = singleMapper ?? throw new ArgumentNullException(nameof(singleMapper));
}
public IEnumerable<TDestination> Map(IEnumerable<TSource> source)
{
var count = source.Count();
var destination = new TDestination[count];
for (int i = 0; i < count; i++)
{
var currentSource = source.ElementAt(i);
var currentDestination = _singleMapper.Map(currentSource);
destination[i] = currentDestination;
}
return destination;
}
}
}
What did I do:
- I added two generic parameters:
TSource
andTDestination
. - Changed
IMapper<NinjaEntity, Ninja>
toIMapper<TSource, TDestination>
, which is now aligned with the class’ generic parameters. - I also renamed variables to align their name with the new more generic aspect of the class.
Now, as long as a single entity mapper exist, we can map a collection, neat right?
As a proof of that, in the NinjaEntityEnumerableToNinjaMapperTest
, the only thing that was updated is the class name:
After hitting the “Run all” button, all tests are still passing!
That said, I will rename NinjaEntityEnumerableToNinjaMapperTest
to EnumerableMapperTest
and move stuff around a little to keep our test suite healthy.
namespace ForEvolve.Blog.Samples.NinjaApi.Mappers
{
public class EnumerableMapperTest
{
public class Map : EnumerableMapperTest
{
public class NinjaEntity2Ninja : Map
{
protected EnumerableMapper<NinjaEntity, Ninja> MapperUnderTest { get; }
protected Mock<IMapper<NinjaEntity, Ninja>> NinjaEntityToNinjaMapperMock { get; }
public NinjaEntity2Ninja()
{
NinjaEntityToNinjaMapperMock = new Mock<IMapper<NinjaEntity, Ninja>>();
MapperUnderTest = new EnumerableMapper<NinjaEntity, Ninja>(NinjaEntityToNinjaMapperMock.Object);
}
[Fact]
public void Should_delegate_mapping_to_the_single_entity_mapper()
{
// Arrange
var ninja1 = new NinjaEntity();
var ninja2 = new NinjaEntity();
var ninjaEntities = new List<NinjaEntity> { ninja1, ninja2 };
NinjaEntityToNinjaMapperMock
.Setup(x => x.Map(It.IsAny<NinjaEntity>()))
.Returns(new Ninja())
.Verifiable();
// Act
var result = MapperUnderTest.Map(ninjaEntities);
// Assert
NinjaEntityToNinjaMapperMock.Verify(x => x.Map(ninja1), Times.Once);
NinjaEntityToNinjaMapperMock.Verify(x => x.Map(ninja2), Times.Once);
}
}
}
}
}
With this refactoring, the test name is more meaningful of its intent: EnumerableMapperTest+Map+NinjaEntity2Ninja.Should_delegate_mapping_to_the_single_entity_mapper
.
To test more types combo, we could extract a generic representation of the test to reuse the code. I will leave you to it because, for now, we do not need other collection mappers.
The end of this article
What have we covered in this article?
In this article:
- We created the Ninja mapping subsystem that we will use in the
NinjaRepository
. - We explored the Façade design pattern.
- We also used our test suite to refactor our subsystem, introducing a more flexible than its predecessor
EnumerableMapper
. - We also ensured that our test suite stays healthy by keeping it up to date.
What’s next?
In the next article:
- We will finally implement the
NinjaRepository
. - We will use Azure Table Storage to store our ninja’s data.
- I will also introduce my in-development ForEvolve Framework that will help us access our data.
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.