实体VS域模型VS视图模型

Nar*_*esh 45 c# architecture asp.net-mvc design-patterns entity-framework

关于这个主题有数百个类似的问题.但我仍然感到困惑,我想得到专家的建议.

我们正在使用ASP.NET MVC 4和EF5开发应用程序,我们的数据库是第一种方法.

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

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

ENTITY <=> DOMAIN模型

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

查看模型<=>域模型<=> ENTITY

我的域名模型真的有必要吗?我不能使用我的实体与Presentation层进行通信.如果我在表示层中引用实体,是否会产生任何影响?如果有什么样的影响?

Ayd*_*din 98

我认为你在定义每个图层的含义以及它在解决方案中扮演的角色时遇到了问题.


数据层

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

// ----------------------------
//    Data tier
//        - MySQL
//        - MS SQL
//        - SharePoint list
//        - Excel
//        - CSV
//        - NoSQL
// ----------------------------
Run Code Online (Sandbox Code Playgroud)

数据访问层

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

请考虑我们的数据源是MS SQL数据库,我们正在使用Entity Framework来访问数据.您将尝试抽象出来的是数据库和实体框架,并且Data Repository每个都有一个Entity.

例...

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

通过使用存储库模式,我们可以抽象出数据访问代码的实现,以便将来,如果我们的数据源发生更改,我们的应用程序的其余部分将不会受到影响.接下来,我们需要一个CustomersRepository在我们Data Access Layer,这将包括诸如Add,RemoveFindById.抽象出任何数据访问代码.下面的例子是你如何实现这一目标.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

数据访问层属于数据层和业务逻辑之间.

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

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

// ----------------------------
//    Data tier
//        - MySQL
//        - MS SQL
//        - SharePoint list
//        - Excel
//        - CSV
//        - NoSQL
// ----------------------------
Run Code Online (Sandbox Code Playgroud)

业务层

业务层构建在数据访问层之上,并不处理任何数据访问问题,而是严格的业务逻辑.如果业务要求之一是阻止从英国境外订购,那么业务逻辑层将处理此问题.


表示层

表示层只是简单地显示您的数据,但是如果您不小心您提供的数据以及允许发布的数据,那么您将自行设置很多令人头疼的问题,这就是为什么它的原因.使用视图模型非常重要,因为视图模型是表示层关注的问题,表示层不需要了解有关域模型的任何信息,只需了解视图模型即可.

那么什么是视图模型......它们只是针对每个视图定制的数据模型,例如注册表单将包括a RegistrationViewModel,暴露这些典型属性.

public class RegistrationViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string ConfirmPassword { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

表示层还处理输入验证,因此例如验证键入的电子邮件地址是否具有正确的格式,或者输入的密码是否是表示层关注点,而不是业务问题,并且可以通过使用来处理Data Annotations.

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; }
}
Run Code Online (Sandbox Code Playgroud)

使用View Models的重要原因是因为Business Models属于业务层,它们包含应保持私有的数据.例如,如果您要在JSON响应中公开域模型,它会向用户公开整个数据,他们的名称是他们的地址,因为您没有选择暴露什么和不暴露什么,而是使用任何好像在起作用.


我还应该指出,domain models和之间存在差异entity models.有已经是进入了很多更详细的答案在这里


横切关注点

我会简短地说明一下:

  • 例外管理
  • 视图模型到域模型的映射.

  • 我一直在寻找几个小时,这就是我一直在寻找的解释.谢谢! (4认同)

Bru*_*sta 9

我不是专家,但我会就这个话题分享我50美分.

查看模型

我想分享您对忽略View模型的担忧.

使用视图模型,您可以:

  1. 从域模型中仅选择所需的数据
  2. 以正确的方式格式化您需要的数据(例如格式价格小数(100.00)到字符串(€100.00))
  3. 您可以在视图模型上使用DataAnnotation.

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

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

实体与域模型

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

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


Geo*_*der 6

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

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

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

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