2011-10-12 50 views
29

我在CQRS应用程序的读取设计中与我的同事进行了辩论。在CQRS中,我的阅读方应该返回DTO还是ViewModels?

选项1:应用读我CQRS应用程序返回的DTO的侧上,例如:

public interface IOrderReadService 
{ 
    public OrderDto Load(int id); 
} 

public class SomeController 
{ 
    public ActionResult SomeAction(int id) 
    { 
     var dto = ObjectFactory.GetInstance<IOrderReadService>().Load(id); 
     var viewModel = Mapper.Map<OrderDto, SomeViewModel>(); 
     return View(viewModel); 
    } 
} 

public class SomeOtherController 
{ 
    public ActionResult SomeOtherAction(int id) 
    { 
     var dto = ObjectFactory.GetInstance<IOrderReadService>().Load(id); 
     var viewModel = Mapper.Map<OrderDto, SomeOtherViewModel>(); 
     return View(viewModel); 
    } 
} 

选项2:该应用程序读取侧返回的ViewModels,例如:

public interface IOrderReadService 
{ 
    public SomeViewModel LoadSomething(int id); 
    public SomeOtherViewModel LoadSomethingElse(int id); 
} 

public class SomeController 
{ 
    public ActionResult SomeAction(int id) 
    { 
     return View(ObjectFactory.GetInstance<IOrderReadService>().LoadSomething(id)); 
    } 
} 

public class SomeOtherController 
{ 
    public ActionResult SomeOtherAction(int id) 
    { 
     return View(ObjectFactory.GetInstance<IOrderReadService>().LoadSomethingElse(id)); 
    } 
} 

从我的同事和我在这件事上所做的研究来看,答案似乎是混合的 - 看起来它确实取决于上下文。所以我问你,亲爱的StackOverflowians:

一种方法似乎比另一种有明显的优势吗?如果是这样,他们是什么?

+1

我要补充一点分歧的主要问题是在编辑画面正在处理聚集根的儿童。 –

+0

对自己的无耻提及:你的问题让我想到了返回ViewModels的后果。感谢那天的食物! http://stackoverflow.com/q/21408226/253098 – SystematicFrank

回答

29

更好的总体意见是每屏(Greg Young的)或每个部件(如果我理解正确乌迪大寒)甚至一个投影一个凸起。

要将读取的模型合并到DTO中,而这些DTO再次必须映射到不同的视图中,这与优化的读取模型的整体目的相矛盾。它增加了我们试图摆脱摆在首位的复杂性和映射步骤。

我的建议:尽量在SELECT * FROM ViewSpecificTable [WHERE ...]或其他类似的地方尽量靠近,如果在瘦读层中使用NoSQL的话。

聚合根和他们的“孩子”的概念在读取方面没有太多的含义,因为这些是领域模型的概念。您不希望在读取方有一个域模型,它只存在于写入端。

Mohamed Abed提到的UI平台特定转换应该在UI层完成,而不是在读取模型本身中完成。

长话短说:我会选择选项2.不要将它与特定于平台的ViewModel混淆,我宁愿将它称为ReadModel,但无论如何都有一个视图。

+0

预测是很难做到这一点,因为我们只有一个关系数据存储,它首先用EF代码完成。最重要的是我不知道如何表达意见。 –

+0

我是否正确理解你的观点:你想从EF-Model中提取你的DTOs/ViewModels以及你在写入方面使用的吗?听起来像是通过映射来自单个模型的数据而在读取和写入模型之间进行人为分离。在这种情况下,使用DTO和ViewModels没有太大的区别,因为无论如何你都失去了读/写分离的固有优势。 –

+0

[附录:]在CQRS中,您不希望在EF-Model之上创建视图(或者至少这只是转换遗留代码的第一步)。你会想直接从数据库建立你的意见。无论是NoSQL,分开的非规范化表格,还是仅仅查看3NF数据。 –

3

读取模型是写入模型的投影。它在那里是为了实现一个特定的目的。在你的情况下,似乎提供视图模型到您的MVC控制器。因此,为什么要将DTO映射到Viewmodels?我能想到的唯一原因是拥有两个独立阅读模型的好处可能不会超过其维护成本。但是,考虑到为了“重用”和“降低维护成本”而“合并”读取模型会增加开发人员的复杂性(我可以更改此表吗?嗯,我现在有两个(或更多)消费者,我必须考虑帐户 - 闻起来有点像数据库整合一遍)。

只是我的想法。

5

我宁愿返回DTO以将应用程序层与演示技术分开(因为每种演示技术可能对演示模型的结构都有一些要求),例如Web MVC应用程序绑定与WPF MVVM绑定不同,可能需要视图模型中的某些属性/字段与应用程序数据无关,例如(SliderWidth或IsEmailFieldEnabled ...)。 另外,例如,如果使用WPF MVVM,我需要实现INotifyPropertyChanged接口以允许绑定,这在实现此应用程序读取服务中的接口方面并不方便,也不相关。

所以我宁愿分离从实际的演示技术和视图模型读取数据表示的关注。

所以选择1对我来说是

+1

在CQRS中,不应该使用视图模型来更改数据。应该发送明确的命令。 –

+1

是的,我已经在谈论只读模型,也许我错了描述的例子(滑块),但我的意思是像(进度条)或任何只读属性被绑定到只读视图 –

12

DDD/CQRS的主要原则之一是,你不应该编辑视图模型。相反,基于任务的屏幕应引导用户发出明确的命令。如果你不能拿出基于任务的屏幕,你应该使用不同的形式,如一个CQRS的我在这里描述:

http://udidahan.com/2011/10/02/why-you-should-be-using-cqrs-almost-everywhere/

+0

我们正在使用基于任务的屏幕。 DTO仅由读取方返回以映射到ViewModels。 –

+0

然后在[HttpPost]控制器动作上,ViewModel被映射到一个命令,然后发送到写入端。 –

相关问题