【问题标题】:In CQRS, should my read side return DTOs or ViewModels?在 CQRS 中,我的读取端应该返回 DTO 还是 ViewModel?
【发布时间】:2011-10-12 23:23:55
【问题描述】:

我正在与我的同事就 CQRS 应用程序读取端的设计进行辩论。

选项 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:

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

【问题讨论】:

  • 我应该补充一点,主要的分歧点在于编辑屏幕正在处理聚合根的子级。
  • 无耻引用自己:你的问题让我想到了返回 ViewModel 的后果。感谢您今天的食物! stackoverflow.com/q/21408226/253098

标签: c# cqrs


【解决方案1】:

一般建议是每个屏幕一个投影(Greg Young)甚至每个小部件一个投影(如果我正确理解 Udi Dahan)。

将读取模型整合到必须再次映射到单独视图中的 DTO 与优化读取模型的整个目的相矛盾。它增加了我们最初试图摆脱的复杂性和映射步骤。

我的建议:如果在瘦读取层中使用 NoSQL,请尽量接近 SELECT * FROM ViewSpecificTable [WHERE ...] 或类似的东西。

聚合根及其“子项”的概念在读取方面没有太多含义,因为这些是域模型概念。您不想在读取端有域模型,它只存在于写入端。

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

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

【讨论】:

  • 预测很难做到,因为我们只有一个单一的关系数据存储,首先使用 EF 代码完成。我不知道如何在此基础上发表意见。
  • 我对您的理解是否正确:您也想从您在写入端使用的 EF 模型中提取您的 DTO/ViewModel 吗?听起来只是通过映射来自单个模型的数据在读取和写入模型之间人为地分离。在这种情况下,使用 DTO 和 ViewModel 没有太大区别,因为无论如何您都失去了读/写分离的固有优势。
  • [附录:] 在 CQRS 中,您不希望在 EF 模型之上创建您的视图(或者至少这只是转换遗留代码的第一步)。您可能希望直接从数据库构建视图。无论是 NoSQL、单独的非规范化表还是仅查看 3NF 数据。
  • 如果可以的话,我想避免最终的一致性(因此单个数据存储)。我不想担心客户将商品添加到订单中,并且在订单刷新时它可能不存在。
  • 如果您在 RDBMS 中的表之上构建视图,则不会有最终的一致性。但是,如果您在基于 ORM 的模型之上构建视图,它与良好的旧分层架构并没有太大区别。
【解决方案2】:

DDD/CQRS 的主要原则之一是您不应该编辑视图模型。相反,基于任务的屏幕应该引导用户发出明确的命令。如果您无法提出基于任务的屏幕,您应该使用另一种形式的 CQRS,就像我在这里描述的那样:

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

【讨论】:

  • 我们正在使用基于任务的屏幕。 DTO 仅由读取方返回,以便映射到 ViewModel。
  • 然后在 [HttpPost] 控制器动作上,ViewModel 被映射到一个命令,然后该命令被发送到写入端。
【解决方案3】:

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

所以我更愿意将读取数据表示的关注与实际的表示技术和视图模型分开。

所以选项 1 对我来说更好

【讨论】:

  • 在 CQRS 中,不应该使用视图模型来更改数据。相反,应该发送明确的命令。
  • 是的,我已经在谈论只读模型,也许我错误描述了示例(滑块),但我的意思是(进度条)或任何要绑定的只读属性只读视图
【解决方案4】:

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

只是我的想法。

【讨论】:

    猜你喜欢
    • 2013-10-04
    • 1970-01-01
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    • 2020-12-17
    • 2014-08-31
    • 1970-01-01
    • 2012-10-27
    相关资源
    最近更新 更多