【问题标题】:Is Entities on Client side anti-pattern?客户端上的实体是反模式吗?
【发布时间】:2014-06-06 12:44:57
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: breeze ria breeze-sharp


    【解决方案1】:

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

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

    客户端不是表示层

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

    术语“实体”和“DTO”对不同的人有不同的含义。我喜欢 Evan's DDD definition 代表“实体”,而 Fowler's definition for "DTO" 代表 PoEAA

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

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

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

    发送实体或 DTO?

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

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

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

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

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

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

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

    何时使用演示模型

    你写道:

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

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

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

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

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

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

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

    服务污染

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

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

    例外处理

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

    当我的应用程序中的 一切 看起来都符合异常时,我会得出结论认为我的架构对于这个特定的应用程序 是错误的......这应该不要成为微风应用程序。我不知道这是不是你的情况。

    希望这会有所帮助。

    【讨论】:

    • 感谢沃德的回答。
    【解决方案2】:

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

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

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

    【讨论】:

    • 嗨。这些教科书没有探讨 DDD 对表示/域模型分层重复自身的丰富的分布式客户端应用程序意味着什么,尽管不一定具有完全相同的规则。您的服务器和客户端模型在共享逻辑(如果不是代码)时分开增长没有问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    • 1970-01-01
    • 2018-04-22
    • 2021-10-03
    相关资源
    最近更新 更多