将 DTO 映射到域对象的最佳实践? ?

我见过很多关于将 DTO映射到域对象的问题,但是我觉得它们没有回答我的问题。我以前用过很多方法,并且有自己的观点,但是我正在寻找一些更具体的东西。

情况:

我们有许多域对象。我们使用的是 CSLA 模型,因此我们的域对象可以非常复杂,并且它们包含自己的数据访问。你不会想在电线上传播这些的。我们将编写一些新的服务,它们将以多种格式返回数据(。Net、 JSON 等)。出于这个(以及其他原因) ,我们还创建了一个精益的数据传输对象,以便在线路上传递。

我的问题是: 应该如何连接 DTO 和 Domain 对象?

我的第一反应是使用 Fowler,DTO 模式类型解决方案。我见过很多次这样做,我觉得这样做是对的。域对象不包含对 DTO 的引用。调用外部实体(“映射器”或“汇编器”)从域对象创建 DTO。通常在域对象端有一个 ORM。这样做的缺点是,对于任何实际情况,“映射器”往往变得极其复杂,并且可能非常脆弱。

提出的另一个想法是让域对象“包含”DTO,因为它只是一个精益数据对象。域对象属性将在内部引用 DTO 属性,并且可以根据请求返回 DTO。我看不出这有什么问题,但感觉不对。我看过一些文章,其中使用 冬眠的人似乎使用这种方法。

还有其他方法吗? 上面的方法之一值得使用吗? 如果有或者没有,为什么?

74809 次浏览

You could use an automapper such as the one written by Jimmy Bogard which has no connection between the objects and relies on naming conventions being adhered to.

A benefit of having a mapper that sits between your domain and your DTO is not as appearent when you are only supporting a single mapping, but as the number of mappings increases, having that code isolated from the domain helps keep the domain simpler and leaner. You won't be cluttering your domain with a lot of extra weight.

Personally, I try and keep the mapping out of my domain entities and put the responsibility in what I call "Manager / Service layer". This is a layer that sits between the application and the respository(ies), and provides business logic such as workflow coordination (If you modify A, you might have to also modify B so service A will work with Service B).

If I had a lot of possible ending formats, I might look at creating a plugable formatter that could use the Visitor pattern, for example to transform my entities, but I've not found a need yet for anything this complex.

You can also try Otis, an Object-to-object mapper. Concepts are similar to NHibernate mapping (attribute or XML).

http://code.google.com/p/otis-lib/wiki/GettingStarted

Another possible solution: http://glue.codeplex.com.

Features:

  • Bidirectional mapping
  • Automatic mapping
  • Mapping between different types
  • Nested mapping and Flattening
  • Lists and Arrays
  • Verification of relations
  • Testing the mapping
  • Properties, Fields and Methods

We use T4 templates to create the mapping classes.

Pro's - human readable code available at compile time, faster than a runtime mapper. 100% control over the code (can use partial methods/template pattern to extend functionality on an ad-hoc basis)

Con's - excluding certain properties, collections of domain objects etc., learning T4 syntax.

I can suggest a tool I created and is open source hosted at CodePlex: EntitiesToDTOs.

Mapping from DTO to Entity and vice-versa is implemented by extension methods, these compose the Assembler side of each end.

You end with code like:

Foo entity = new Foo();
FooDTO dto = entity.ToDTO();
entity = dto.ToEntity();


List<Foo> entityList = new List<Foo>();
List<FooDTO> dtoList = entityList.ToDTOs();
entityList = dtoList.ToEntities();

How do you see to implement a constructor inside the DTO class that takes as a parameter a domain object?

Say... Something like this

class DTO {


// attributes


public DTO (DomainObject domainObject) {
this.prop = domainObject.getProp();
}


// methods
}

Another option would be to use ModelProjector. It supports all possible scenarios and is very easy to use with minimal footprint.

We can use Factory, Memento, and Builder pattern for that. Factory hide the details on how to create instance of domain model from DTO. Memento will take care the serialization/deserialization of the domain model to/from DTO and can even access private members. The Builder will allows mapping from DTO to domain with fluent interface.

Keeping the mapping logic inside of your entity means that your Domain Object is now aware of an "implementation detail" that it doesn't need to know about. Generally, a DTO is your gateway to the outside world (either from an incoming request or via a read from an external service/database). Since the entity is part of your Business Logic, it's probably best to keep those details outside of the entity.

Keeping the mapping somewhere else would be the only alternative - but where should it go? I've tried introducing mapping objects/services but it seemed like overengineering (and probably was) after all was said and done. I've had some success using Automapper and such for smaller projects but tools like Automapper come with their own pitfalls. I've had some pretty hard-to-find issues related to mappings because Automapper's mappings are implicit and completely decoupled from the rest of your code (not like "separation of concerns" but more like a "where does the godforsaken mapping live") so they can sometimes be hard to track down. Not to say that Automapper doesn't have its uses, because it does. I just think mapping should be something that is as obvious and transparent as possible to avoid issues.

Instead of creating a mapping service layer, I've had a lot of success keeping my mappings inside of my DTOs. Since DTOs always sit at the boundary of the application, they can be made aware of the Business Object and figure out how to map from/to them. Even when the number of mappings scales to a large amount it works cleanly. All the mappings are in one place and you don't have to manage a bunch of mapping services inside your Data Layer, Anticorruption Layer, or Presentation Layer. Instead, the mapping is just an implementation detail delegated to the DTO involved with the request/response. Since serializers generally only serialize properties and fields when sending it across the wire, you shouldn't run into any issues. Personally, I've found this the cleanest option and I can say, in my experience, it scales nicely on a large code base.

If the amount of mappings scales to an unreasonable amount (which has yet to happen to me in over 10 years) then you can always create a mapping class that lives close to your DTOs.