Reader small image

You're reading from  Architecting ASP.NET Core Applications - Third Edition

Product typeBook
Published inMar 2024
Reading LevelIntermediate
PublisherPackt
ISBN-139781805123385
Edition3rd Edition
Languages
Right arrow
Author (1)
Carl-Hugo Marcotte
Carl-Hugo Marcotte
author image
Carl-Hugo Marcotte

Carl-Hugo Marcotte is a software craftsman who has developed digital products professionally since 2005, while his coding journey started around 1989 for fun. He has a bachelor's degree in computer science. He has acquired a solid background in software architecture and expertise in ASP.NET Core through creating a wide range of web and cloud applications, from custom e-commerce websites to enterprise applications. He served many customers as an independent consultant, taught programming, and is now a Principal Architect at Export Development Canada. Passionate about C#, ASP.NET Core, AI, automation, and Cloud computing, he fosters collaboration and the open-source ethos, sharing his expertise with the tech community.
Read more about Carl-Hugo Marcotte

Right arrow

Object Mappers

In this chapter, we explore object mapping. As we saw in the previous chapter, working with layers often leads to copying models from one layer to another. Object mappers solve that problem.

We first look at manually implementing an object mapper, because understanding the basics is very important to progress further. Then, we improve our design by regrouping the mappers under a mapper service, exploring the Aggregate Services pattern, and creating a mapping façade along the way, reusing the Façade pattern once more.

Finally, we replace that manual work with two open-source tools that help us generate business value instead of writing mapping code.

In this chapter, we cover the following topics:

  • The Object Mapper pattern
  • Code smell – too many dependencies
  • Overview of the Aggregate Services pattern
  • Implementing a mapping façade
  • Implementing a mapping service
  • Exploring AutoMapper
  • Exploring...

The Object Mapper pattern

What is object mapping? In a nutshell, it is the action of copying the value of an object’s properties into the properties of another object. But sometimes, properties’ names do not match; an object hierarchy may need to be flattened and transformed.

As we saw in the previous chapter, each layer can own its own model, which can be a good thing, but that comes at the price of copying objects from one layer to another. We can also share models between layers, but even then, we usually need to map one object onto another. Even if it’s just to map your models to Data Transfer Objects (DTOs), object mapping is almost inevitable.

Remember that DTOs define our API’s contract. Independent contract classes help maintain the system, making us choose when to modify them. If you skip using DTOs, each time you change your domain model, it automatically updates your endpoint’s contract, possibly breaking some clients....

Code smell – too many dependencies

Using this kind of mapping could become tedious in the long run, and we would rapidly see scenarios such as injecting three or more mappers into a single request delegate or controller. The consumer would likely already have other dependencies, leading to four or more injected dependencies.

That should raise the following question:

  • Does the class do too much and have too many responsibilities?

In this case, the fine-grained IMapper interface pollutes our request delegates with tons of dependencies on mappers, which suggests that the class may be doing more than it should, likely violating the Single Responsibility Principle by carrying an excessive number of responsibilities. This can lead to several downsides, such as:

  • Reduced readability: A class with too many dependencies becomes complex and harder to understand
  • Decreased maintainability: Changes in one part of the code may have unexpected effects...

Implementing a mapping façade

We studied façades already; here, we explore another way to organize our many mappers by leveraging that design pattern.

Instead of what we just did, we create a mapping façade to replace our aggregate services. The code consuming the façade is more elegant because it uses the Map methods directly instead of the properties. The responsibility of the façade is the same as the aggregate, but it implements the interfaces instead of exposing them as properties.

This example shares a lot of the same code as the aggregate services example we just explored, so we start to look at the new code that replaces the IProductMappers interface and the ProductMappers class:

using Shared.Contracts;
using Shared.Mappers;
using Shared.Models;
namespace MappingFacade;
public interface IProductMapperService :
    IMapper<Product, ProductSummary>,
    IMapper<InsertProduct, Product>,
    IMapper<UpdateProduct, Product>...

Implementing a mapping service

This section explores another way to organize objects. We will end up with a similar interface to the previous example but a completely different implementation.

This example aims to simplify the implementation of the mapper façade with a universal interface. To achieve this, we are implementing the diagram shown in Figure 13.3. Here’s a reminder:

Figure 15.5: Object mapping using a single IMapper interface

Instead of naming the interface IMapper, we use the name IMappingService. This name is more suitable because it is not mapping anything; it is a dispatcher servicing the mapping request to the right mapper. Let’s take a look:

namespace Core.Mappers;
public interface IMappingService
{
    TDestination Map<TSource, TDestination>(TSource entity);
}

That interface is self-explanatory; it maps any TSource to any TDestination.

On the implementation side, we are leveraging the Service Locator pattern...

Exploring AutoMapper

We just covered different ways to implement object mapping, but here we leverage an open-source tool named AutoMapper that does it for us instead of implementing our own.

Why bother learning all of that if a tool already does it? There are a few reasons to do so:

  • It is important to understand the concepts; you don’t always need a full-fledged tool like AutoMapper. Moreover, if you work in a regulated enterprise, you don’t have the luxury of loading every NuGet package you want and must go through a vetting process for each one.
  • It allowed us to cover multiple patterns that we can use in other contexts and apply them to components with different responsibilities. So, all in all, you should have learned multiple new techniques during this object mapping progression.
  • Lastly, we dug deeper into applying the SOLID principles to write better programs.

Project – AutoMapper

The AutoMapper project is also a copy...

Exploring Mapperly

Mapperly is a newer object mapper library that leverages source generation to make it lightning-fast. There are many ways to create object mappers with Mapperly and many options to adjust the mapping process. The following project is similar to the others but using Mapperly.

We cover the following ways to use Mapperly:

  • Injecting a mapper class
  • Using a static method
  • Using an extension method

Source generators were introduced with .NET 5, enabling developers to generate C# code automatically during the compilation process. These powerful tools serve as an advanced feature that taps into the Roslyn compiler pipeline, allowing for the creation and injection of source code into a project before it is compiled. Source generators help eliminate boilerplate code, ensure type safety, and improve performance by pre-calculating complex or static values. Unlike traditional code generation tools that create additional files, source...

Summary

Object mapping is an unavoidable reality in many cases. However, as we saw in this chapter, there are several ways of implementing object mapping, taking that responsibility away from the other components of our applications or simply coding it inline manually.

At the same time, we took the opportunity to explore the Aggregate Services pattern, which gives us a way to centralize multiple dependencies into one, lowering the number of dependencies needed in other classes. That pattern can help with the too-many-dependencies code smell, which, as a rule of thumb, states that we should investigate objects with more than three dependencies for design flaws.

When moving dependencies into an aggregate, ensure there is cohesion within the aggregate to avoid adding unnecessary complexity to your program and just moving the dependencies around.

We also explored leveraging the Façade pattern to implement a mapping façade, which led to a more readable and elegant...

Questions

Let’s take a look at a few practice questions:

  1. Is it true that injecting an Aggregate Service instead of multiple services improves our system?
  2. Is it true that using mappers helps us extract responsibilities from consumers to mapper classes?
  3. Is it true that you should always use AutoMapper?
  4. When using AutoMapper, should you encapsulate your mapping code into profiles?
  5. How many dependencies should start to raise a flag telling you that you are injecting too many dependencies into a single class?

Further reading

Here are some links to build upon what we learned in the chapter:

  • If you want more information on object mapping, I wrote an article about that in 2017, titled Design Patterns: ASP.NET Core Web API, Services, and Repositories | Part 9: the NinjaMappingService and the Façade Pattern: https://adpg.link/hxYf
  • AutoMapper official website: https://adpg.link/5AUZ
  • AutoMapper’s Design Philosophy is an excellent article that explains why the tool was created and what use case it is good at, written by the library’s author: https://adpg.link/mK2W
  • AutoMapper Usage Guidelines is an excellent do/don’t list to help you do the right thing with AutoMapper, written by the library’s author: https://adpg.link/tTKg
  • Mapperly (GitHub): https://adpg.link/Dwcj

Answers

  1. Yes, an Aggregate Service can improve a system, but not necessarily. Moving dependencies around does not fix design flaws; it just moves those flaws elsewhere.
  2. Yes, mappers help us follow the SRP. However, they are not always needed.
  3. No, it is not suitable for every scenario. For example, when the mapping logic becomes complex, consider not using AutoMapper. Too many mappers may also mean a flaw in the application design itself.
  4. Yes, use profiles to organize your mapping rules cohesively.
  5. Four or more dependencies should start to raise a flag. Once again, this is just a guideline; injecting four or more services into a class can be acceptable.

Learn more on Discord

To join the Discord community for this book – where you can share feedback, ask questions to the author, and learn about new releases – follow the QR code below:

https://packt.link/ArchitectingASPNETCoreApps3e

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Architecting ASP.NET Core Applications - Third Edition
Published in: Mar 2024Publisher: PacktISBN-13: 9781805123385
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at £13.99/month. Cancel anytime

Author (1)

author image
Carl-Hugo Marcotte

Carl-Hugo Marcotte is a software craftsman who has developed digital products professionally since 2005, while his coding journey started around 1989 for fun. He has a bachelor's degree in computer science. He has acquired a solid background in software architecture and expertise in ASP.NET Core through creating a wide range of web and cloud applications, from custom e-commerce websites to enterprise applications. He served many customers as an independent consultant, taught programming, and is now a Principal Architect at Export Development Canada. Passionate about C#, ASP.NET Core, AI, automation, and Cloud computing, he fosters collaboration and the open-source ethos, sharing his expertise with the tech community.
Read more about Carl-Hugo Marcotte