1

我之前使用过 RIA 服务,现在正在测试 Breeze Sharp。

RIA 和 Breeze 给人的印象是,您在服务器/中间层看到的就是您在客户端看到的。为了支持这一点,在客户端和服务器上都使用了术语实体。它真的是一个实体,还是客户端上的一个演示模型或模型?

对于具有一层或两层实体图的较小系统,认为客户端和服务器是相同的可能没有错。对于图形深入到五个或六个级别的大型系统,需要将实体转换为 DTO 以使其变得简单。除非 UI 有一些实体的 CRUD 屏幕,否则大型应用程序最终会有更多的 DTO 和更少的实体。大多数时候,这些 DTO 将代表 UI 想要的内容,并且相当于一个演示模型。

为什么我们不能将我们在客户端处理的内容视为表示模型而不是实体?

4

2 回答 2

15

您可以随意调用客户端实体类:-)

更严肃地说,让我们来看看声称这是一种反模式背后的典型推理。

客户端不是表示层

我想非常清楚这一点。Breeze 专为富 Web 客户端应用程序而设计。Breeze 客户端不是表示层;它一个表示层。它还拥有自己的业务模型和数据访问层。

术语“实体”和“DTO”对不同的人意味着不同的东西。我喜欢Evan对“实体”的 DDD 定义和Fowler在PoEAA中对“DTO”的定义

Breeze 客户端实体符合 Evans 实体的条件:“具有贯穿时间和不同表示的独特身份的对象。您还听说过这些称为‘参考对象’ ” [ Fowler ]。Breeze 实体不仅仅是财产包;它们也有业务逻辑,你可以用你自己的更多来扩展它们。

Breeze 实体不是“表示模型”。它们独立于任何特定的 UI 表示,并且通常不实现表示问题。

它们设计使得它们可以直接绑定到视觉控件。这是 Breeze 生产力设计决策……关于我们如何实现实体的决策。有些人——那些认为实体属性是反模式的人——会讨厌这一点。埃文斯对这个问题保持沉默。福勒便便。如果它冒犯了你,你可能不喜欢微风。向前走。

发送实体还是 DTO?

我要争辩说这是一种错误的二分法。

人们常说“通过网络发送实体是一种反模式。总是发送 DTO ”。这个措辞不佳的法令背后有充分的理由。当客户端和服务器实体类相同时,您已将服务器的实现与客户端的实现耦合。如果模型在服务器上更改,它必须在客户端更改,反之亦然,即使更改仅与其中一个层相关。这可能会干扰您独立发展服务器和客户端代码的能力。我们可以接受这种耦合是一种权宜之计(权宜之计很重要!),但没有人想要它。

Breeze客户端实体类在形状和业务逻辑上都不必服务器实体类相同。当您在 Breeze 中查询时,您将实体数据放在网络上并将其转换为客户端实体;保存时,您将客户端实体数据放在网络上,并将其在服务器上转换为服务器实体。DTO 可能涉及任一方向。重要的事实是类可以不同。

当然,它们在概念上是相关的。Customer如果实体的含义在两侧大相径庭,您将很难在两种表示之间转换数据。无论有没有明确的 DTO,都是如此。

让我们承认,当类实际上相同时,在两个方向上转换数据会更容易。当它们不同时,您需要支付映射税,并且您可能会失去在客户端上编写 Breeze LINQ 查询的能力。如果你愿意,你可以交税。微风不在乎。

我倾向于从双方相同的课程开始,并在必要时更改它们。这对于 RIA Services 和 DevForce 中的大部分课程都非常有效。最重要的是,在需要时重新分解为单独的类对我来说从来都不是一件难事。

<rant>担忧夸大了共享类定义的风险并低估了映射层的成本,这些映射层的好处在应用程序的生命周期中很少在实践中实现。</rant>

何时使用演示模型

你写了:

对于图形深入到五个或六个级别的大型系统,需要将实体转换为 DTO 以使其变得简单。...大多数时候,这些 DTO 将代表 UI 想要的内容,并且相当于一个演示模型

根据我的经验,只有当您假设您的客户只是将实体粘贴到屏幕上时,这才是正确的。但是我已经规定了客户端是一个应用程序,而不是表示层。

我进一步认为,您在客户端需要一个域模型,原因与您在服务器上需要一个域模型的原因相同:推理域。您可以独立于演示文稿执行此操作。我假设您的实体将以某种方式出现在多个屏幕上,并遵循不同的呈现规则。它是相同的模型,呈现出多种方式。我们称之为“围绕数据旋转”。

无论您在模型上放置多少张面孔,底层模型数据和管理它们的业务规则都应该保持不变。这就是使它成为“领域模型”而不是“演示模型”的原因。

FWIW,我的应用程序中总是有一个“演示模型”(又名“ViewModel”)来编排视图的活动。所以我不会问自己“PM还是模特?”。相反,我选择将可视控件直接数据绑定到我通过 VM 的 api 公开的模型实体,或者将它们绑定到包装一些实体的中间“项目表示模型”(又名“项目视图模型”)。我走哪条路是申请决定。在实践中,我首先直接绑定到实体,并在需要时重构为“Item ViewModel”。

无论哪种情况,我都会在客户端上构建我需要的 PM(VM)。如果我需要一个“Item ViewModel”,我也会在客户端上创建它。我不要求我的服务器准备 DTO 以供我的客户端显示。对我来说,这是一种反模式,因为它将服务器耦合到客户端

如何?如果开发人员需要更改客户端上的屏幕,她可能必须等待有人提供支持的服务器端点和 DTO。现在我们必须协调服务器和客户端的发布计划,即使改变的动力是客户端要求,而不是服务器要求。

服务污染

实际上比这更糟糕。一些服务器端开发人员不得不停止她正在做的事情并添加一个新的服务方法来满足客户的需求。这不是她的要求之一……但现在是。随着时间的推移,服务 API 会极大地扩展,很快就会出现很多看起来很像的成员,他们以略微不同的方式完成相同的工作。

最终,我们忘记了谁在使用哪种方法以及为了什么。没有人敢改变现有的方法,因为害怕破坏一个未知的客户。所以开发者复制了一些看起来正确的东西,让它有点不同,然后称之为别的东西。任何使用过企业应用程序的人都应该熟悉这种服务 API 污染模式。

例外处理

每一个表面上的“规则”都注定要被打破。当然,有时让服务器为显示准备数据既方便又高效。这种情况最常发生在大量只读数据中,这些数据汇总了数据层上大量的复杂数据。当我走这条路时,我通常是出于性能考虑。否则,我会坚持面向实体的架构。

当我的应用程序中的所有内容看起来符合异常时,我会得出结论,我为这个特定的应用程序设计了错误的架构……而且这不应该是 Breeze 应用程序。我不知道这是不是你的情况。

希望这可以帮助。

于 2014-06-09T22:19:23.777 回答
0

我不知道我是否喜欢在客户端拥有另一个域模型的想法。这将导致我们多年来一直在努力解决的传奇问题,即将业务逻辑分发给客户端。我的意思是前端,如 Windows 窗体或 XAML 应用程序或 HTML5,前提是应用程序连接到服务器。如果实体只存在于服务端的领域层,并且被应用服务屏蔽,是不是可以维护?

正如我从 DDD 教科书中了解到的,当模型是实体时,它具有行为和数据。表示模型只关注服务器上的域实体的表示。这些模型已经过验证。这可以很简单,因为业务规则验证需要电子邮件。当客户端处理复杂的业务规则验证时,它可能使用已在域层中实现的相同规则。像 RIA 这样的框架的优点是可以将这些业务规则共享给客户端,因此可以避免重复。这也将强制将相同的实体推送到客户端。

我赞成客户端模型和服务器模型分开增长的想法——在客户端模型和服务器上的域实体模型。所涉及的成本是模型的翻译、印迹应用程序服务、验证业务规则是否复杂的往返。您在回答中提到了其中一些。

于 2014-06-13T16:12:16.703 回答