1

这个问题确实具有更大的架构含义,我欢迎对此提出任何意见或建议:

在 OOP 方面,我更像是 Martin Fowler 的思想流派。我相信您应该能够直接在 UI 中呈现域实体。如果我有一个 Car 实体,我应该能够将它呈现到网页上。领域模型是一个横切关注点,而不是一个层。将领域模型视为一个层会导致领域模型贫乏。我不相信 OOP 架构中的 DTO。

对我来说,视图模型是一种组合视图中所需的域实体的方式。这不是 DTO。我不明白使用像 DTO 这样的视图模型背后的原因是什么,尽管使用 automapper 似乎很常见?

因此,使用元数据方法,我将数据注释放在我的域模型上,以提供有关如何呈现和验证实体的任何 UI 实现提示。我喜欢拥有更丰富的领域模型。

在 MVC3 中,如何使用驻留在 UI 层中的资源文件来完成此操作(特别是使用显示数据注释)?是否有本机实现,还是我需要自己发挥创造力?还是我在方法的某个地方出错了?

4

2 回答 2

1

我不同意。

一方面,您将用于指定实体属性应如何在网页上显示的一些属性来自 System.Web 命名空间,而不是 System.ComponentModel.DataAnnotations 命名空间。通过将这些属性放在域模型中的属性上,您的域模型将依赖于 System.Web。例如,有一个 [HiddenInput] 属性告诉 MVC3 将字段呈现为输入类型 =“隐藏”。这不在 System.CompoenentModel.DataAnnotations 中。

其次,我认为您不需要实体属性上的数据注释属性来拥有丰富的域模型。丰富的领域模型来自将知识包装在上下文中的类。客户端应用程序不需要知道任何关于域的信息就可以使用它。您可以通过使用通用语言描述知识的类、方法和属性来实现丰富的领域模型。DataAnnotations 属性不适合普遍存在的语言 imo。而且,您的域不仅仅是您的实体。您可以使用工厂、服务和其他模式来构建丰富的域模型。只有实体和元数据的域对我来说听起来很乏味。

第三,您可能有一个实体,应该在您的网站上以不同的方式呈现。当有人搜索汽车时,您可能只想显示品牌、型号、年份和缩略图。当有人点击搜索结果时,您可能希望显示多张照片、评论等。如果您要在实体上使用 UIHint 属性来告诉 web ui 如何渲染汽车,您将无法拥有在不同的上下文中渲染汽车的不同策略。

最后,是的,automapper 非常适合将实体 DTOing 到视图模型中。它本质上允许您填充实体的副本,与域断开连接,针对特定的 UI 问题。在这里使用 HiddenInput 和 UIHint 属性来告诉 MVC3 如何渲染数据是安全的。

对评论 1 的回应

至于 UIHint,我在这里提到它是因为它与 MVC3 EditorTemplates 有特殊的含义。在部分视图涉及接收输入的情况下,视图的组成是什么?通常对应于某些聚合根中的实体及其属性的文本字段、下拉列表和输入元素。因此,您将需要一些实体的表示来封装数据。您的 DTO 也可以是具有深度的聚合根。您可以拥有一个具有标量属性(文本/日期/布尔)、导航属性(下拉列表)和集合属性(ul/ol/table)的根 DTO。

我们为聚合根中的许多实体创建相应的视图模型,并使用 EditorTemplates 将它们实现为视图。如果我们想要切换到不同的 EditorTemplate,我们可以将 UIHint 应用于 viewmodel 属性。因此我们可以告诉它“将位置 dto 渲染为谷歌地图”。Automapper 可以将导航和集合属性映射到相应的视图模型,形成用户所需的域实体的复杂表示。

如果我误解了 flat dto 的意思,请原谅我。

对评论 2 的回应

如果您的要求需要,视图模型 dto 可以展平/非规范化某些属性(使用自动映射器)。例如,考虑一个大学实体。它可能有多种语言(翻译)的许多名称,暗示聚合中的 UniversityName 实体,其中 University 具有名称集合 (1..n)。在这些名称中,1 可能代表 OfficialName / NativeName,另一个可能代表用户 CurrentUICulture 的 TranslatedName。集合中的其他实体可能表示用户不理解的 TranslatedNames,并且不需要被打扰。

如果您的视图只对集合中的这 2 个名称感兴趣,则可以将它们提升为视图模型上的一流属性:

public class UniversityViewModel
{
    public string OfficialName { get; set; }
    public string TranslatedName { get; set; }
    // ...other properties
}

在这种情况下,在转换为视图模型 dto 时对实体的部分进行非规范化是有意义的。注意视图模型是如何贫乏的——从控制器到视图的数据传输的裸容器。这很好,事实上,这是鼓励的。

回答原始问题

要回答您最初的问题,如果您将域模型和实体视为一个层 - 更具体地说,是一个底层,它会有所帮助。如果您将应用程序中的各种关注点视为依赖于其他关注点,则分层软件更容易理解。MVC3 是一个表示/UI 层,并且将依赖于它下面的层——其中一个是您的域层。

如果您想从域层访问 UI 中的资源文件,您将走相反的方向。您将使低层依赖于较高层。如果您的域库依赖于资源的 UI 库,而 UI 库依赖于实体的域,那么您最终会得到循环依赖。我认为如果需要,您可能可以使用反射来完成它,但在这种情况下,您将与框架作斗争。如果是这种情况,MVC 和 .NET 通常可能不是您的最佳选择。

我实际上认为资源文件是一个横切关注点。我们的应用程序到处都是 i18n,我们经常发现我们在域和 UI 中需要相同的语言文本资源。

将 Display 属性放在实体上没有任何问题。但是,如果您想为此使用资源,则将该资源放在域层中,或者如果您觉得它不属于那里,则将其放在较低的层中。这样,域和 UI 都可以访问它。

于 2011-12-18T18:20:34.053 回答
0

所以我最终在域模型中放置了一个资源文件并添加了一个自定义 HiddenFieldAttribute,这样我就不必在域模型中引用 MVC 程序集了。

我仍然从根本上不同意视图模型实际上是 DTO,并且应该将域模型构建为一个层。我觉得以这种方式构建应用程序会创建真正没有价值的抽象。如果域模型真的是一个层,那么我们将构建一组逻辑接口来访问它,而我们不这样做。这是一个跨领域的问题。

Thanks to olivehour for an interesting discussion and suggesting that it's okay to place resource file(s) in to domain model assembly.

于 2012-01-10T18:03:38.147 回答