Entities VS Domain Models VS View Models

2019-01-16 02:04发布

问题:

There are hundreds of similar questions on this topic. But I am still confused and I would like to get experts advise on this.

We are developing an application using ASP.NET MVC 4 and EF5 and ours is DB first approach.

We have Data layer in a separate project which is a class library and holds all the Entities defined in it. And then Business Layer defined with all the repositories and domain models(is that right term to be used). And then comes presentation layer.

At present we have not defined any view models, we are using the same domain models from BL as view models. In this approach one mapping can be enough.

ENTITY <=> DOMAIN MODEL

But for me it's not looking like a good design. I prefer to have view models defined in my presentation layer and use domain models to communicate between presentation layer and business layer. And at BL, convert domain objects to data entities and communicate with DAL. Using this approach I have to use mapping twice.

View Model <=> DOMAIN MODEL <=> ENTITY

Is my domain model really necessary? Can't I use my entity to communicate with Presentation layer. Are there any impacts if I am referencing Entities in my presentation layer? If there are what kind of impacts?

回答1:

I think you're just having issues with defining what each layer is and what role it is playing in your solution.


Data Tier

Your data tier is simply your database / SharePoint list / .csv file / excel sheet... you get the idea, it's simply where your data is stored, and it can be in any format. So remember that the data tier is nothing more than just data.

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

Data Access Layer

This layer abstracts away your data source, and provides an API in which the rest of your application can interact with the data source.

Consider that our data source is an MS SQL Database and that we're using Entity Framework to access the data. What you'll be attempting to abstract away, is the database and Entity Framework, and have a Data Repository for each Entity.

Example...

We have a Customers table in a MS SQL Database. Each customer in the customers table is an Entity , and is represented as such in your C# code.

By using the repository pattern, we can abstract away the implementation of the data access code, so that in future, if our data source changes, the rest of our application wont be affected. Next we would need a CustomersRepository in our Data Access Layer, which would include methods such as Add, Remove and FindById. To abstract away any data access code. The example below is how you would achieve this.

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
    }
}

The data access layer belongs in between the data layer and business logic.

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

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

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

Business layer

The business layer is built on top of the data access layer, and does not deal with any data access concerns, but strictly business logic. If one of the business requirements was to prevent orders being made from outside of the UK, then the business logic layer would handle this.


Presentation tier

The presentation tier simply presents your data, but if you're not careful about what data you present, and what data you allow to be posted, you'll set your self up for a lot of headaches down the line, which is why it's important to use view models, as view models are a presentation tier concern, the presentation tier doesn't need to know anything about your domain models, it only needs to know about view models.

So what are View Models... They're simply data models which are tailored for each view, for example a registration form would include a RegistrationViewModel, exposing these typical properties.

public class RegistrationViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string ConfirmPassword { get; set; }
}

The presentation tier also handles input validation, so for instance validating whether an email address typed has the correct format, or that the passwords entered match is a presentation tier concern, not a business concern, and can be handled by using 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; }
}

The reason it's important to use View Models is because the Business Models belong to the business layer, and they include data which should remain private. For instance, if you were to expose the Domain Model in a JSON response, it would expose the users entire data, their name their address as you're not being selective about what is being exposed and what isn't, but using whatever that seems to be working.


I should also point out here that there is a difference between domain models and entity models. There's already an answer that goes into a lot more detail here


Cross cutting concerns

I'll keep this brief:

  • Exception management
  • Mapping of View Models to Domain Models.
    • AutoMapper


回答2:

I'm not an expert, but I will share my 50 cents on the topic.

View Model

I would like to share your concern about ignoring the View Model.

Using view model you can:

  1. Select only the data you need from the domain model
  2. Format the data you need in the right way to the presentation (e.g. format price decimal (100.00) to string (€100.00))
  3. You can use DataAnnotation on your view model.

So, I also consider it a bad design, but others might have a different opinion.

Remember, the business layer doesn't know anything about the view model, so you should map it in the controller.

Entities vs domain model

I would start it simple, using a POCO as domain model that can be persisted with an ORM or a NoRM. For most of software developed in the world, it will not hurt your system much and it's also simple.

In the future, if you start using web services for some reason, you might need to consider the use of DTOs (Data Transfer Objects) for remote calls. When there, what you can do is to have another layer, responsible for mapping your domain model to the desired DTO. This layer would be used only in the remote call (web service), keeping the view model for the presentation.



回答3:

The main benefit of introducing additional classes is separation of concerns:

  • Presentation layer: display information and data entry, including any pre- and postprocessing to achieve this, e.g. formatting.
  • Domain / Business / Application logic: here the actual work is done
  • Persistence layer: store and retrieve data

This can be achieved with only two model classes for ViewModels and Domain Entities. Instead of modelling the domain logic with separate model classes that are similar to the persisted entities, implement it in service classes that consume the domain entities. The domain entities should at most have logic that deals with their own properties (e.g. to keep the combined values of two properties in a valid state). If more than one domain entity is affected by an usecase, model this usecase in a service class. If you put logic to manage the relationship between entities directly in the entity classes, the code becomes unmaintainable very quickly (trust me, I tried).

I do not recommend using the persistable entities as ViewModels, because this would mix display concerns (e.g. [DisplayName]) with persistence concerns (e.g. [ForeignKey]).