6

so this is C# but it could apply to any OO language where different sets of objects exist across different layers of an application.

We have been trying to come up with a convention within the team for where to put the code when mapping objects in one layer to objects in another layer. For example; mapping a Data Transfer Object to a Service Object that is used by a WCF service, or something of the like. I have the following methods that I have seen being used and am trying to decide which would be preferred by someone new looking at the code, and which of these fits best with the natural C# idioms.

These examples use AutoMapper to map an object Foo to an object Bar, but it applies to any mapping framework, or just manually creating Bar from Foo with an object initialiser. Both these objects represent the same data, but they are in different assemblies:

1) Constructor on Bar

public class Bar
{
    public Bar(Foo foo)
    {
        Mapper.Map(foo, this);
    }
}

2) Factory method on Bar

public class Bar
{
    public static Bar CreateFrom(Foo foo)
    {
        return Mapper.Map<Bar>(foo);
    }
}

3) Method on Foo to return Bar

public class Foo
{
    public Bar GetBar()
    {
        return Mapper.Map<Bar>(this);
    }
}

4) Explicit type conversion operator on Foo so it can be casted to Bar

public class Foo
{
    public static explicit operator Bar(Foo foo)
    {
        return Mapper.Map<Bar>(foo);
    }
}

5) Some implementation of the factory pattern

public class BarFactory : IFactory<Bar>
{
    public Bar Create(Foo foo)
    {
        return Mapper.Map<Bar>(foo);
    }
}

6) Just doing it in the piece code you need it in

    public void SomeMethod()
    {
        Foo foo = fooRepository.GetFoo();

        //I need it to be bar now...
        Bar bar = Mapper.Map<Bar>(foo);

    }

Disclaimer: I wish I hadn't used AutoMapper in this example. Just imagine "AutoMapper" here reads "IMappingProvider" or "IObjectConverter" or something

8
  • use of Automapper is rapidly becoming an anti-pattern. This last week every question seems to have a reference to it.
    – gbjbaanb
    Jul 10, 2014 at 10:59
  • What would the alternative be though? I did think of writing a T4 template to create the mapping code but it just seemed a bit NIH. Also, this is just an example, we aren't actually calling Mapper.Map in these places we have abstracted that through a IMapProvider interface Jul 10, 2014 at 11:34
  • 1
    Regarding 1-4, this is a form of coupling. Why does Foo need to know about Bar or vice-versa? The fact that you need to map from one to the other is something only the client code needs to care about.
    – Doval
    Jul 10, 2014 at 11:44
  • I see your point, but we usually do this in situations where coupling is already present in one direction. For example, a WCF service is coupled to a domain model. So we are mapping from an object that only the service knows about, to a domain object that it is already coupled. Jul 10, 2014 at 12:01
  • 1
    @horrorcat Automapper just seems to be too fashionable, an answer to any question. I would map them manually. Yup, it involves boring work, but you don't (or shouldn't) have to have every field in your WCF objects, you introduce an overly tight coupling then that is not based on a designed API. Ie you have a DB schema and a client, and you're assuming the client needs to access the DB objects. That's wrong, it should talk to your WCF tier that in turn uses only those parts of the DB it needs to fulfill that API. A simple, mapped, pass-through is not best practice.
    – gbjbaanb
    Jul 10, 2014 at 12:17

3 Answers 3

4

In all cases, I'd prefer #5. In the 1-2-3-4 sample of code, I think that the domain object doesn't have to know DTO objects related to him.

Let's say you have

public class User
{
    ... some properties ...
} 

public class UserThumbnail
{
    ... some properties ...
} 

A Domain Model object (User) doesn't have to know that a DTO object (UserThumbnail) is using User's properties, because it adds coupling between these classes, and the responsibility of creating the DTO object (UserThumbnail) is added to Domain object (User).

So if the use cases of your business doesn't specify that a User should be able to create a UserThumbnail, then I think that you should not do it. That's why classes like Factory should be used in this case: because DTO objects doesn't pollute domain objects. If a factory is used in that case, the domain object doesn't even know that DTO object exists.

For the #6, if you'd ever change AutoMapper to another mapping tool, then you'd have to search in your code. If you used a Factory, you know that all AutoMapper calls are there.

Mapper.Map is doing 2 things : Create instance and map properties. Factory Pattern is the solution if you want to isolate object creation code.

2

I would write a mapper class and manually map the data from one type to the other. Yeah it's boilerplate, but you are fully in control, it's simple and it does the job. I would keep it out of the model assembly as your model objects shouldn't have any knowledge of the WCF returned types.

public class FooMapper
{
    public Foo Map(Bar bar)
    {
        return new Foo
            {
                ValueOne = bar.ValueOne,
                ValueTwo = bar.ValueTwo,
                ....
            };
    }
}
1

Normally I'd prefer #3 - GetBar is very obvious where its come from and what you're getting, but for this case, mapping classes I would often use the conversion operator.

But.. mapping doesn't seem like you're casting, you're really creating something new that is not contained within the object (where I'd use GetBar), just converting it (operator bar).

So in this case, I'd use the CreateFrom - its obvious you're making a new mapped object to represent something else that is related to Foo and not a Foo or a contained or inherited subset of Foo in any shape or form.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.