【问题标题】:How to convert a DTO to Domain Objects如何将 DTO 转换为域对象
【发布时间】:2016-09-21 14:52:15
【问题描述】:

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

我想将来自客户端的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 来解决问题。请仔细阅读最后一部分。'

谢谢。

【问题讨论】:

    标签: domain-driven-design dto


    【解决方案1】:

    典型的答案是将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;
    }
    

    【讨论】:

    • 您的答案是完美的,但您知道RegistrantType.Person() 在这种特定情况下返回枚举值 0 和 RegistrantType.Company() 返回枚举值 1。我没有将数字传递到聚合中的原因是为了避免泄漏域的内部。如果我在这种特定情况下创建工厂,工厂应该再次返回一个数字作为枚举标志。这对我来说听起来像是一个隐含和贫血的模型。有什么建议吗?
    【解决方案2】:

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

    例如

    class CompanyRegistration : Registration {
    
    }
    
    class PersonRegistraiton : Registration {
    
    }
    

    那么你可以使用继承而不是你的 if/else 场景

    public class Aggregate {
      public Aggregate (CompanyRegistration) {
         registantType = RegistrantType.Company();      
      }
    
      public Aggregate (PersonRegistration p) {
         registrantType = RegistrantType.Person(); 
      }
    
    
    }
    

    您可以为 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)
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-22
      • 1970-01-01
      • 1970-01-01
      • 2018-05-29
      • 1970-01-01
      • 2021-07-14
      • 2020-02-29
      • 1970-01-01
      相关资源
      最近更新 更多