AutoMapper's Design Philosophy

While a lot of people use AutoMapper, and love it, I meet just as many people that hate it. When I hear their stories, it becomes clear to me that it's not that AutoMapper was "abused" per se, but that it was used without understanding why AutoMapper exists and what problems it was designed to solve.

AutoMapper originated at the beginning of a large MVC application way back in the early days on MVC. Back then, there really wasn't any guidance about what exactly the "M" in "MVC" should be. Most MVC frameworks have a strong concept of a model - in Rails, Django, and many others, the M is a first-class citizen. The joke in ASP.NET MVC is the "M" is silent.

So we adopted the name "view model" to describe our models in MVC - these were models specifically designed for a view.

We started this long-running project with a few rules for our view models:

  • Each view model would be designed for one and only one view
  • Only the information needed to render or model bind is contained on the view model

With these rules in place, we started building screens. A few dozen screens in, we started to notice a problem.

Bespoke models

As we built screens, we needed to build out our view model types. We knew we wanted to create view models per screen, but what past that? How should we name the type? How should we name the members?

We found that nearly all of our screens were just subsets of data from a richer model. We had a lot of boring assignment code:

var order = dbContext.FindById(id);
var orderDto = new OrderDto {
    Id = order.Id,
    CustomerName = order.Customer.FullName,
    LineItems = order.LineItems.Select(li => new OrderDto.OrderLineItem 
    {
        Id = li.Id,
        ProductName = li.Product.ShortName,
        Description = li.Product.Description,
        Count = li.Count,
        Price = li.ItemPrice
    }).ToList();
};

I noticed a couple of things:

  • DTO names were arbitrary. Sometimes we called it "model", sometimes "Dto"
  • Member names were shortened/abbreviated arbitrarily

There wasn't any rhyme or reason behind this, it was whatever the developer decided to do. On top of this, we would get null reference exceptions fairly frequently - when there was missing data for whatever reason, our simple assignment expressions would blow up (this was also before the null conditional operator).

On top of this, we would have to unit test all of this - making sure all of the properties were populated appropriately, and nothing was missing.

Putting these two together, and it was going to be a recipe for disaster for an app that would eventually have nearly 1000 screens.

Enter AutoMapper

The architect showed me all this (I was tech lead on the project at the time) and said "fix it". The main problems as I saw it were:

  • The view models were all subtly, but pointlessly, different
  • The code to handle sparse models or missing data was error-prone and often missed
  • The tests for all those assignments was easy to get wrong

Was there any business value in having a property named "Price" instead of "ItemPrice"? And since we worked so hard on the original model names, adhering to the ubiquitous language of our broader team, why was it then OK for the developer to take shortcuts in the view model design?

With this in front of me, I set out to build a tool that:

  • Enforced a convention for destination types
  • Removed all those null reference exceptions
  • Made it super easy to test

And thus AutoMapper was born.

AutoMapper's Design Philosophy

AutoMapper works because it enforces a convention. It assumes that your destination types are a subset of the source type. It assumes that everything on your destination type is meant to be mapped. It assumes that the destination member names follow the exact name of the source type. It assumes that you want to flatten complex models into simple ones.

All of these assumptions come from our original use case - view models for MVC, where all of those assumptions are in line with our view model design. With AutoMapper, we could enforce our view model design philosophy. This is the true power of conventions - laying down a set of enforceable design rules that help you streamline development along the way.

By enforcing conventions, we let our developers focus on the value add activities, and less on the activities that provided zero or negative value, like designing bespoke view models or writing a thousand dumb unit tests.

And this is why our usage of AutoMapper has stayed so steady over the years - because our design philosophy for view models hasn't changed. If you find yourself hating a tool, it's important to ask - for what problems was this tool designed to solve? And if those problems are different than yours, perhaps that tool isn't a good fit.