我正在尝试实现一个查询,以便从域模型管理的数据库中获取一些数据投影到 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原则并且没有域泄漏到查询中。我看到人们只返回域对象并使用映射来填充视图模型。可能这是要走的路。谢谢你的帮助。
答案 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.
}
}
但就像我说的那样,我没有在实际项目中尝试过,我认为当缓存的数据没有准备好时,它至多是一个临时解决方案。