自我验证和映射DTO和DDD

时间:2013-11-21 17:30:48

标签: c# domain-driven-design

我第一次接触到DDD,大多数情况下,这似乎是好东西,但有一些事情困扰着我。

目前的主要观点是对象验证自己。

以前,我会写一个与此类似的服务方法:

public class MyService
{
    private IValidator _validator;
    private IDomainMapper _domainMapper;
    private IThingThatDoesSomething _thingThatDoesSomething;
    private IResponseMapper _responseMapper;

    public MyService(IValidator validator, IDomainMapper domainMapper, IResponseMapper responseMapper)
    {
        _validator = validator;
        _domainMapper = domainMapper;
        _responseMapper = responseMapper;
    }

    public ResponseDTO DoSomething(RequestDto requestDto)
    {
        if (_validator.IsValid(requestDto))
        {
            var domainObject = _domainMapper.Map(requestDto);

            var domainResponse = _thingThatDoesSomething.DoSomething(domainObject);

            return _responseMapper.Map(domainResponse);
        }
        else
        {
            return new ResponseDTO { Valid = false, Errors = /* some error information */ };
        }
    }
}

然而,花费更多时间研究DDD的同事们更喜欢将验证和映射功能放在域对象上。所以DTO看起来像:

public class RequestDto
{
    public string Something {get; set; }

    public DomainObject Map() 
    {
        return new DomainObject { something = this.Something };
    }

    public bool IsValid()
    {
        return this.Something == "something valid";
    }
}

这种感觉真的错了。首先,对象现在有多重责任,其次,从域名驱动的角度来看,它似乎是错误的,因为我不希望有一封信到达我的桌面,宣称自己有效或不知道如何将自己转换为其他内容

有人可以解释为什么DDD有好处吗?

1 个答案:

答案 0 :(得分:2)

首先,我认为您的原始应用程序服务代码看起来更好而不应用您的同事建议,这根本不是DDD相关的。请记住,无论您是使用DDD还是编写n层CRUD应用程序,基本编码原则始终适用。特别是对于您的原始代码我的意思是:

  1. 您有一个单独的验证类 - 这很好,因为您可以重复使用它。
  2. 你有一个单独的类来映射/到数据对象 - 这很好,因为域对象不应该被任何映射细节所困扰,特别是当它映射到数据对象时。
  3. 另一方面,有一些事情可以在DDD方面做得更好:

    1. 您的映射器显然可以以两种方式映射(从域对象到数据对象,反之亦然)。在DDD域中,正在工厂中创建/实现对象(不强制执行特定的工厂实现)或/和在存储库中。重要的事实是域对象创建是域本身的责任,而不是应用程序服务(如在您的代码中)。

    2. 您正在使用验证程序验证数据对象,但我不确定您是否在域中进行了相同的输入验证。在DDD中,很多人采用的方法可以用句子概括:“永远不要让你的域进入无效状态”。我分享这种方法。在我的项目中,如果验证逻辑很复杂,我倾向于为实体提供单独的验证器。域实体本身使用此验证器来验证输入参数。对于简单的验证(例如,null和空字符串检查),我将其留在域对象中。至于您的数据对象验证,大多数人(包括我)倾向于尽可能“靠近”用户界面以获得更好的用户体验(例如,更快的响应和验证错误)。

    3. 值得一提的另一件事是,根据您的上一段代码片段,它认为您可能在某些时候误解了您的同事。您的RequestDTO类不是域对象,所以即使在从同事那里得到一些建议之后,也不应该将验证或映射逻辑放在其中。 DTO是数据持有者,仅此而已。

      TL; DR版

      1. 从域对象映射到数据对象是应用程序层的责任(可以在单独的映射器类中实现)。
      2. 不应在应用层中通过映射器从数据对象映射域对象。创建域对象是域本身的责任(通过工厂)。
      3. 始终验证进入您域的数据。 永远不要让您的域名陷入无效状态。实体的验证逻辑应该放在域内。