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 will create a mapping façade to replace our aggregate services. The code consuming the façade will be 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.Let’s look at the code:
public interface IProductMapperService :
IMapper<Product, ProductDetails>,
IMapper<InsertProduct, Product>,
IMapper<UpdateProduct, Product>
{
}
public class ProductMapperService : IProductMapperService
{
private readonly IMapper<Product, ProductDetails> _entityToDto;
private readonly IMapper<InsertProduct, Product> _insertDtoToEntity;
private readonly IMapper<UpdateProduct, Product> _updateDtoToEntity;
// Omitted constructor injection code
public ProductDetails Map(Product entity)
{
return _entityToDto.Map(entity);
}
public Product Map(InsertProduct dto)
{
return _insertDtoToEntity.Map(dto);
}
public Product Map(UpdateProduct dto)
{
return _updateDtoToEntity.Map(dto);
}
}
In the preceding code, the ProductMapperService class implements the IMapper interfaces through the IProductMapperService interface and delegates the mapping logic to each injected mapper: a façade wrapping multiple individual mappers. Next, we look at the ProductsController that consumes the façade:
public class ProductsController : ControllerBase
{
private readonly IProductMapperService _mapper;
// Omitted constructor injection, other methods, routing attributes, …
public ProductDetails GetProductById(int id)
{
Product product = …; // Fetch a product by id
ProductDetails dto = _mapper.Map(product);
return dto;
}
}
From the consumer standpoint (the ProductsController class), I find it cleaner to write_mapper.Map(…) instead of _mapper.SomeMapper.Map(…). The consumer does not want to know what mapper is doing what mapping; it only wants to map what needs mapping. If we compare the Mapping Façade with the Aggregate Services of the previous example, the façade takes the responsibility of choosing the mapper and moves it away from the consumer. This design distributes the responsibilities between the classes better.This was an excellent opportunity to review the Façade design pattern. Nonetheless, now that we’ve gone through multiple mapping options and examined the issue of having too many dependencies, it’s time to move forward on our object mapping adventure with an enhanced version of our mapping façade.
Leave a Reply