使用DDD将域逻辑泄漏到MVC.NET应用程序中的查询中

时间:2013-10-04 15:42:15

标签: c# asp.net-mvc entity-framework domain-driven-design cqrs

我正在尝试实现一个查询,以便从域模型管理的数据库中获取一些数据投影到 MVC视图

我已经读过,返回静态视图的MVC控制器应该从查询处理程序或所谓的读取模型存储库请求 DTO 而不是使用聚合根存储库返回完整的成熟域对象。这样我们可以最大限度地提高性能(优化对所需数据的查询)并降低域模型滥用的风险(我们不会意外地使用DTO更改模型)。

问题是某些 DTO属性无法直接映射到数据库表字段,并且可能会根据某些业务规则填充或是一些在DB中没有隐含声明的条件的结果。这意味着查询会对从域泄漏的某些逻辑起作用。我听说这是不对的,查询应该直接过滤,排序,预测和汇总数据库表中的数据(在我的情况下使用linq查询和EF)。

到目前为止,我设想了2个解决方案:

1)读取模型存储库在内部查询完整的域模型对象,使用它们填充 DTO属性(重要的是需要一些业务逻辑的那些他们使用)。在这里,当我们对实例化的域模型采取行动时,我们不会获得性能优势。

2)另一个解决方案是缓存数据库中所有需要的数据,可能是域存储库(处理聚合根),因此查询会对数据字段进行操作(具有缓存值) )无需解决域逻辑。然后,缓存数据的一致性将由域存储库维护,这也会产生一些开销。

示例:

1)业务规则可以像某些对象或数据(跨系统)的字符串表示一样简单,即格式化;

2)业务规则可以计算字段返回bool,如下面的简单域模型:

// first aggregate root
public class AssignedForm
{
    public int Id {get;set}
    public string FormName {get;set}
    public ICollection<FormRevision> FormRevisions {get;set}
    public bool HasTrackingInformation
    {
        get
        {
           return FormRevisions.Any(
                      fr=>   fr.RevisionType==ERevisionType.DiffCopy 
                             && fr.FormRevisionItems.Any)
        }
    }

    public void CreateNextRevision()
    {
         if(HasTrackingInformation)
         {
         .......
         }
         .......
    }
}

public enum ERevisionType { FullCopy=0,DiffCopy=1 }

public class FormRevision
{
   public int Id {get;set}
   public ERevisionType RevisionType {get;set}
   public ICollection<FormRevisionItem> FormRevisionItems {get;set}
}

然后我们有一个读取模型库,比如 IFormTrackingInfoReader 返回对象集合

public class FormTrackingInfo
{
   public int AssignedFormId {get;set}
   public int AssignedFormName {get;set}
   public bool HasTrackingInformation {get;set}
}

问题是如何实现 IFormTrackingInfoReader 并填充 HasTrackingInformation 属性坚持DRY原则并且没有域泄漏到查询中。我看到人们只返回域对象并使用映射来填充视图模型。可能这是要走的路。谢谢你的帮助。

1 个答案:

答案 0 :(得分:0)

我不喜欢解决方案1,域模型不是持久无知。

就个人而言,我更喜欢解决方案2。但“永远需要的数据”可能是一个问题。如果出现新的查询要求,也许你需要一些数据迁移(我听说事件重放会在使用事件源时发挥作用)。所以我认为有一个混合解决方案:使用值对象来实现派生。我们可以使用dto创建新的值对象实例。

public class SomeDto {

    public String getSomeDerivation() {
         return new ValueObject(some data in this dto).someDerivation();
    }
}

在这种情况下,我认为域逻辑受到保护并与持久性分离。但我没有在实际项目中尝试过这个。

<强> UPDATE1:

混合解决方案不适合您特定的FormTrackingInfo案例,但您的解决方案有两个。一个例子是(对不起,我不是Java中的.net人)

public class CustomerReadModel {
     private String phoneNumber;
     private String ....//other properties

     public String getPhoneNumber() {
         return phoneNumber;
     }

     public String getAreaCode() {
         return new PhoneNumber(this.phoneNumber).getAreaCode();//PhoneNumber is a value object.
     }
}

但就像我说的那样,我没有在实际项目中尝试过,我认为当缓存的数据没有准备好时,它至多是一个临时解决方案。