实体 VS 领域模型 VS 视图模型

Posted

技术标签:

【中文标题】实体 VS 领域模型 VS 视图模型【英文标题】:Entities VS Domain Models VS View Models 【发布时间】:2014-08-26 15:38:02 【问题描述】:

关于这个主题有数百个类似的问题。但我仍然很困惑,我希望得到专家的建议。

我们正在使用 ASP.NET MVC 4 和 EF5 开发应用程序,我们的方法是 DB 优先。

我们在一个单独的项目中有数据层,它是一个类库,并包含其中定义的所有实体。然后使用所有存储库和域模型定义业务层(这是要使用的正确术语)。然后是表示层。

目前我们还没有定义任何视图模型,我们使用来自 BL 的相同领域模型作为视图模型。在这种方法中,一个映射就足够了。

实体域模型

但对我来说,它看起来并不是一个好的设计。我更喜欢在表示层中定义视图模型,并使用域模型在表示层和业务层之间进行通信。在 BL,将域对象转换为数据实体并与 DAL 进行通信。使用这种方法,我必须使用两次映射。

查看模型 域模型 实体

我的领域模型真的有必要吗?我不能使用我的实体与表示层进行通信。如果我在表示层中引用实体有什么影响吗?如果有什么样的影响?

【问题讨论】:

您可以使用视图模型映射实体 @EhsanSajjad:感谢您的评论。当然我可以使用,但我想了解更多关于直接在我的表示层中使用引用实体的优点和缺点。 ORM Entities vs. Domain Entities under Entity Framework 6.0 的可能重复项 @GertArnold - 感谢您的评论。 相关帖子 - what is difference between a Model and an Entity & Entity vs Model vs View Model 【参考方案1】:

我认为您只是在定义每一层是什么以及它在您的解决方案中扮演什么角色时遇到问题。


数据层

您的数据层就是您的数据库/SharePoint 列表/.csv 文件/excel 表...您明白了,它就是您的数据的存储位置,它可以是任何格式。所以请记住,数据层只不过是数据。

// ----------------------------
//    Data tier
//        - mysql
//        - MS SQL
//        - SharePoint list
//        - Excel
//        - CSV
//        - NoSQL
// ----------------------------

数据访问层

这一层抽象出您的数据源,并提供一个 API,您的应用程序的其余部分可以在其中与数据源进行交互。

假设我们的数据源是一个 MS SQL 数据库,并且我们使用实体框架来访问数据。您将尝试抽象出的是数据库和实体框架,并且每个 Entity 都有一个 Data Repository

示例...

我们在 MS SQL 数据库中有一个Customers 表。 customers 表中的每个客户都是一个 Entity ,并且在您的 C# 代码中表示为这样。

通过使用存储库模式,我们可以抽象出数据访问代码的实现,这样以后,如果我们的数据源发生变化,我们应用程序的其余部分就不会受到影响。接下来,我们需要在Data Access Layer 中添加CustomersRepository,其中包括AddRemoveFindById 等方法。抽象出任何数据访问代码。下面的示例是您将如何实现这一点。

public interface IEntity

    int Id  get; set; 


public class Customer : IEntity

    public int Id  get; set; 
    public string FirstName  get; set; 
    public string LastName  get; set; 
    public DateTime RegistrationDate  get; set; 


public interface IRepository<TEntity> where TEntity : class, IEntity

    TEntity FindById(int id);
    
    void Add(TEntity entity);
    
    void Remove(TEntity entity);


public class CustomerRepository : IRepository<Customer>

    public Customer FindById(int id)
    
        // find the customer using their id
        return null;
    
    
    public void Add(Customer customer)
    
        // add the specified customer to the db
    
    
    public void Remove(Customer customer)
    
        // remove the specified customer from the db
    

数据访问层位于数据层和业务逻辑之间。

// ----------------------------
//    Business logic
// ----------------------------

// ----------------------------
//    Data access layer
//        - Repository 
//        - Domain models / Business models / Entities
// ----------------------------

// ----------------------------
//    Data tier
//        - MySQL
//        - MS SQL
//        - SharePoint list
//        - Excel
//        - CSV
//        - NoSQL
// ----------------------------

业务层

业务层建立在数据访问层之上,不处理任何数据访问问题,而是严格的业务逻辑。如果其中一项业务要求是阻止来自英国以外的订单,那么业务逻辑层将处理此问题。


展示层

展示层只是展示您的数据,但如果您不注意展示的数据以及允许发布的数据,那么您将面临很多令人头疼的问题,这这就是为什么使用视图模型很重要,因为视图模型是表示层的关注点,表示层不需要了解您的领域模型的任何信息,它只需要了解视图模型。

那么什么是视图模型...它们只是为每个视图量身定制的数据模型,例如,注册表将包含 RegistrationViewModel,公开这些典型属性。

public class RegistrationViewModel

    public string Email  get; set; 
    public string Password  get; set; 
    public string ConfirmPassword  get; set; 

表示层还处理输入验证,例如验证输入的电子邮件地址是否具有正确的格式,或者输入的密码是否匹配是表示层的问题,而不是业务问题,可以使用@987654339 处理@。

public class RegistrationViewModel

    [Required]
    [DataType(DataType.EmailAddress)]
    public string Email  get; set; 

    [Required]
    [DataType(DataType.Password)]
    [Compare("ConfirmPassword")
    public string Password  get; set; 

    [Required]
    [DataType(DataType.Password)]
    public string ConfirmPassword  get; set; 

使用视图模型很重要的原因是因为业务模型属于业务层,它们包含应该保持私有的数据。例如,如果您要在 JSON 响应中公开域模型,它将公开用户的整个数据,他们的名字和地址,因为您不需要选择公开的内容和不公开的内容,而是使用任何内容似乎工作正常。


我还要在这里指出domain modelsentity models 之间的区别。已经有一个更详细的答案here


横切关注点

我会保持简短:

异常管理 视图模型到域模型的映射。 AutoMapper

【讨论】:

我一直在寻找几个小时,这就是我一直在寻找的解释。谢谢! 在实现上述存储库模式时,IRepository、IEntity、Customer 和 CustomerRepository 是进入 DAL 还是业务层? @VanceSmith 我可能应该将上面的代码更新为this,因为它更通用,您可以将它与您的任何/所有实体一起使用...来解决您的问题:它将存在于数据层 @AydinAdn:“跨领域关注”部分非常简短。我担心在使用 DTO 映射持久对象时避免/最小化麻烦。我已经为此提出了单独的问题。 ***.com/questions/40720883/… 因此,对于没有数据呈现给客户端的简单控制台应用程序,不需要视图模型,更不用说映射实体了。我们只是将实体从存储库暴露给业务层,在那里进行计算,然后传递打算再次持久化的数据。我说的对吗?【参考方案2】:

我不是专家,但我会在这个主题上分享我的 50 美分。

查看模型

我想分享您对忽略视图模型的担忧。

使用视图模型,您可以:

    从域模型中仅选择您需要的数据 以正确的方式将您需要的数据格式化为演示文稿(例如,将价格小数 (100.00) 格式化为字符串 (€100.00)) 您可以在视图模型上使用 DataAnnotation。

所以,我也认为这是一个糟糕的设计,但其他人可能会有不同的看法。

请记住,业务层对视图模型一无所知,因此您应该将其映射到控制器中。

实体与域模型

我会从简单的开始,使用 POCO 作为可以使用 ORM 或 NoRM 持久化的域模型。对于世界上开发的大多数软件来说,它不会对你的系统造成太大的伤害,而且它也很简单。

将来,如果您出于某种原因开始使用 Web 服务,您可能需要考虑使用 DTO(数据传输对象)进行远程调用。 在那里,您可以做的是拥有另一层,负责将您的域模型映射到所需的 DTO。该层将仅用于远程调用(Web 服务),保留演示文稿的视图模型。

【讨论】:

感谢您的回复。 感谢您的帖子。我开始了解NoRM - 一个连接到无 SQL 数据库的 ORM。【参考方案3】:

引入额外类的主要好处是关注点分离:

表示层:显示信息和数据输入,包括实现此目的的任何预处理和后处理,例如格式化。 领域/业务/应用程序逻辑:这里完成了实际工作 持久层:存储和检索数据

这可以通过 ViewModels 和 Domain Entities 的两个模型类来实现。不要使用类似于持久化实体的单独模型类对域逻辑进行建模,而是在使用域实体的服务类中实现它。域实体最多应该具有处理它们自己的属性的逻辑(例如,将两个属性的组合值保持在有效状态)。如果一个用例影响了多个领域实体,则在服务类中对该用例进行建模。如果您将管理实体之间关系的逻辑直接放在实体类中,那么代码很快就会变得无法维护(相信我,我试过了)。

我不建议将可持久化实体用作 ViewModel,因为这会将显示问题(例如 [DisplayName])与持久性问题(例如 [ForeignKey])混合在一起。

【讨论】:

以上是关于实体 VS 领域模型 VS 视图模型的主要内容,如果未能解决你的问题,请参考以下文章

业务领域建模Domain Modeling

请问VS中的ADO.NET实体数据模型怎么添加?我在VS的联网模版里没找到

领域模型中的实体类分为四种类型:VODTODOPO

VS2015 在添加新的实体数据模型时抛出异常

VS2012导入Sqlite数据库实体模型

视图和实体框架