如何将DTO转换为域对象

时间:2016-09-21 14:52:16

标签: domain-driven-design dto

我正在尝试将无处不在的语言应用于我的域对象。

我想将来自客户端的Data Transfer Object转换为域对象。 Aggregate's Constructor仅接受必填字段,其他参数应使用aggregate's API传递,即使正在创建Aggregate时(例如CreateAggregate command)。

但是DTOAggregate映射代码变得有点混乱:

if(DTO.RegistrantType == 0){
    registrantType  = RegistrantType.Person()
}
elseif(DTO.RegistrantType == 1){
    registrantType  = RegistrantType.Company()
}
//.....
//.....
var aggregate = new Aggregate(
        title,
        weight,
        registrantType,
        route,
        callNumber,
    )

//look at this one:

if(DTO.connectionType == 0){
    aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
    aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
//..........
//..........

我应该提到的一件事是这个问题似乎不是特定领域的问题。

如何在不放弃domain internals leakage的情况下减少这些If-Else语句,并确保聚合(不是映射工具)不接受可以使其无效的值商业规则,并应用无处不在的语言?

请不要告诉我,我可以使用AoutoMapper来做这个伎俩。请仔细阅读最后一部分。'

谢谢。

2 个答案:

答案 0 :(得分:2)

一个典型的答案是将DTO(实际上是一条消息)转换为Command,其中该命令将所有参数表示为特定于域的值类型。

void doX(DTO dto) {
    Command command = toCommand(dto)
    doX(command)
}

void doX(Command command) {
    // ...
    aggregate.Route(command.connectionType)
}

toCommand逻辑使用类似Builder模式的东西来提高代码的可读性是相当常见的。

if(DTO.connectionType == 0){
    aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
    aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}

在像这样的情况下,策略模式可以提供帮助

ConnectionTypeFactory f = getConnectionFactory(DTO.connectionType)
ConnectionType connectionType = f.create(DTO)

一旦你认识到ConnectionTypeFactory是一个东西,你可以考虑构建查找表以选择正确的。

Map<ConnectionType, ConnectionTypeFactory> lookup = /* ... */

ConnectionTypeFactory f = lookup(DTO.connectionType);
if (null == f) {
    f = defaultConnectionFactory;
}

答案 1 :(得分:1)

那么为什么不使用更多继承

例如

class CompanyRegistration : Registration {

}

class PersonRegistraiton : Registration {

}

然后您可以使用继承而不是if / else方案的

public class Aggregate {
  public Aggregate (CompanyRegistration) {
     registantType = RegistrantType.Company();      
  }

  public Aggregate (PersonRegistration p) {
     registrantType = RegistrantType.Person(); 
  }


}

你可以应用simmilar逻辑来表示setRoute方法或任何其他大的if / else情况。

另外,我知道你不想听,你可以编写自己的映射器(在aggegate内)映射并验证它的业务逻辑

例如,这个想法来自fluentmapper

var mapper = new FluentMapper.ThatMaps<Aggregate>().From<DTO>()
                    .ThatSets(x => x.title).When(x => x != null).From(x => x.title)

编写自己的映射器并不难,允许这种规则并验证您的属性。我认为它会提高可读性