On Automappers



This post is about some bad practices in using automappers.

On Automappers

Automappers can be a great addition to libraries you use in your project, especially if its domain is not too complicated or you just need to get the ball rolling when you are starting out. You can quickly lay out a simple model, write a couple of lines to map it to DTOs, and that’s it:

var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>());

var mapper = config.CreateMapper();

You then can use it in your code base like this:

public class OrderController

{

    public void Submit(OrderDto orderDto)

    {

        Order order = _mapper.Map(orderDto); // From DTO to model

        _repository.Save(order);

    }

 

    public OrderDto GetDetails(int id)

    {

        Order order = _repository.GetById(id);

        OrderDto orderDto = _mapper.Map(order); // From model to DTO

        return orderDto;

    }

}

Automappers can provide a huge boost in terms of development speed. However, there are two caveats you need to know of.

First of all, never use them to map DTOs to domain classes. That is what you see in the Submit method of the above code sample. The reason here is that in order to perform such a mapping, you need to bypass all validations and invariant checks of the destination class. And that is an awful thing to do. It means you have to forgo encapsulation and treat your domain classes as mere data storage objects.

Encapsulation is one of the most important attributes of your domain model. It helps you ensure that all invariants are met and the model itself is consistent. Without it, domain classes become just another version of Data Transfer Objects. In the case above, we already have such an object: OrderDto.

The only exception from this guideline is when your domain model is trivial. Although, in this case, it’s probably not even worth creating corresponding domain classes as you most likely can get away with the DTOs themselves.

That brings us to the second use case: mapping from domain classes to DTOs (the GetDetails method in the sample above). This is where automappers provide the most value.

However, this scenario has its limits too.

Let’s say for example that you introduced a complex Entity class:

public class Customer : Entity

{

    private readonly string _name;

    public CustomerName Name => (CustomerName)_name;

 

    private readonly string _primaryEmail;

    public Email PrimaryEmail => (Email)_primaryEmail;

 

    private string _secondaryEmail;

    public Maybe<Email> SecondaryEmail

    {

        get { /* … */ }

        protected set { /* … */ }

    }

 

    public EmailingSettings EmailingSettings { get; protected set; }

 

    /* … */

}

 

public class EmailingSettings : ValueObject<EmailingSettings>

{

    public Industry Industry { get; }

    public bool EmailingIsDisabled { get; }

    public EmailCampaign EmailCampaign => GetEmailCampaign(Industry, EmailingIsDisabled);

 

    /* … */

}

As you can see, it has four Value Objects attached (Name, PrimaryEmail, SecondaryEmail, and EmailingSettings). Also, one of them – SecondaryEmail – is represented using the Maybe struct which helps you, as a programmer, explicitly state that the property can turn into null.

Will automappers help you map such an entity to a DTO? In other words, convert all those Value Objects back into primitive types? Not really. They can perform the mapping of course, but that would require setting up quite sophisticated mapping rules which diminishes the value of having the automapper in the first place. After all, automappers are supposed to do most of the work automatically.

It turns out that automappers aren’t that good in complex domains. In scenarios similar to the one above, they require you to do as much work as you would do without them. I would even say that in this concrete example, a mapping implemented manually would be easier to maintain due to its explicitness.

Note that in some cases the data you load from the database can completely bypass the domain model. In this case, you don’t need an automapper, nor do you need to involve the domain classes. You can map the data directly to your DTOs using a light-weight ORM like Dapper or low-level ADO.NET. That is what you would normally do if you practice CQRS.

Also note that while I’m using Automapper in my samples, the guidelines apply to any similar library, be it the said Automapper, TinyMapper, and so on.

Summary

Alright, that was a quite short post. Let’s summarize:

  • Never use automappers to map DTOs to domain classes.
  • Use them only to map from domain classes to DTOs.
  • Automappers might not add much value in complex domain models. In such scenarios, you can as well just implement the mapping manually.
  • Automappers are still useful as scaffolding mechanism when you start your project out, or if your domain isn’t too complex.
Share




  • jbogard

    Great list!

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thanks 🙂

  • Andrew Whittam

    I use a commercial grid control in the client app, which requires OData controllers. I’ve followed much of you advice in setting up the domain and my mapping configuration files have become complex as you explain. However, I think AutoMapper’s queryable extensions feature makes it absolutely worthwhile no matter how complex the configuration.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      I agree, this feature makes AutoMapper stand out. It’s now not just an auto-mapping library but also a library that enables approaches similar to CQRS. Which is really handy.

  • http://davidkeaveny.blogspot.com David Keaveny

    Any chance you could expand on, with examples, what you think is the best approach for creating domain entities from your DTOs?

  • Piotr

    Really nice write up! I have a situation where my domain is very simple, it’s basically CRUD type REST Service. So far I was mapping using AutoMapper between DTO and Entity Framework classes (I have no domain classes at all). It worked well until yesterday, when a slightly more complex requirement came to enrich data with some additional computations.

    Firstly I’ve tried to customize my mappings to accommodate the new requirement, but I’m not skilled enough with AutoMapper API, so I gave up and used manual mapping. I still don’t see the need for domain classes as the basic validation is done by ASP.NET Core validation mechanism (attribute based validation), but moving to manual mapping solved the problem in an instant.

    So seems like not using auto mapping tools might be indeed a valid choice.