我不同意。
一方面,您将用于指定实体属性应如何在网页上显示的一些属性来自 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 都可以访问它。