ASP.NET Core 2.2 & 3 REST API #20 — Domain to Contract Mapping with AutoMapper
Up Next: Validating Requests with FluentValidation
We’re going to talk about the different layers our application currently is made of and which parts can be improved, so they can be maintainable and versioned.
- The Controllers should only operate with
Contract
objects (XRequest
—XResponse
) - The Services should only operate with
Domain
objects - (Ideally) The Data Services should only operate with
XDto
(Data Transfer Objects) (We have not implemented this yet, due to simplicity reasons)
We’re going to see how to setup object mapping from one layer to another without having too much complexity. The reason for that is: You want to be able to freely change the structure of your in-memory objects without affecting your contracts (and not breaking your api consumers), or your database structure.
We’re going to see how the first part is being done Domain<->Contracts
. The same principles apply to any other mapping between other layers.
Let’s take a look at our Post
domain object:
While serving the response, you don’t need many of these:
- The EF-Core related attributes
[Key]
and[ForeignKey]
- The virtual
List<PostTag>
neither needs to be virtual, nor needs to be a list (should be aIEnumerable
). - The same things apply for the
PostTag
domain object. We need aPostTagResponse
as well.
The responses should look like this:
These are much simpler than the beefy domain objects and also decoupled from them. Proper contracts.
Then, for every contract object in the controller, you can do the mapping:
You can already tell, that this is a very bad practise for the following reasons:
- For the slightest change, you need to make changes by hand, to each and every such snippet, on every controller and every service.
- Code will not necessarily break, but the functionality will
- Thus, code is not reusable nor maintainable and prone to mistakes
AutoMapper
This can be made much better with AutoMapper, since we no longer return any domain objects.
There are many opinions regarding the use of AutoMapper. We’re going to stick with a quote of it’s creator, Jimmy Bogard: ‘If you can automatically map based on naming 75% of your object, you should use AutoMapper. If not, you should use some custom mapping logic.’
In this case, we can just map anything without any custom logic.
Install 2 packages:
AutoMapper
AutoMapper.Extensions.Microsoft.DependencyInjection
(for service registration)
Just services.AddAutoMapper(typeof(Startup))
. The typeof(Startup)
is an Assembly Marker , meaning that the AutoMapper is going to scan the assembly which Startup.cs
is into and resolve any mapping profiles automatically, registering everything in the container.
Let’s create a new directory, Mapping
, creating a new profile, called DomainToResponseProfile.cs : Profile
.
There are 2 things we need to map:
Post(Domain)
→PostResponse
Tag(Domain)
→TagResponse
However, we’ve got to map the virtual List<PostTag>
on our post object.
We can either create a new map from PostTag
→ TagResponse
, or just manually do it.
In order to demonstrate the flexibility of AutoMapper, we are going to do it the manual way (manual does not imply hard).
Simply define our destination ( IEnumerable<Tag>
) and the way that this is mapped from the source object, in simply 2 lines of code.
We are free to use our IMapper
interface right now, to transform all the unmaintainable code we mentioned above, into 1-liners:
var postResponses = _mapper.Map<List<PostResponse>>(posts);
(List maps don’t need to be explicitly registered)- We can do the same for anything else.
var tagResponses = _mapper.Map<List<TagResponse>>(tags);
- Or for single POCOs.
var postResponse = _mapper.Map<PostResponse>(post);
That’s it.
You can verify that everything still works like the previous videos (nothing is broken).
Of course you can do the same for Contract
→ Domain
mapping and Domain
← → Dto
.
Code is available on Github and the instructional videos are located on YouTube.
Keep Coding