What is the best way to build view model?

2019-04-08 02:39发布

问题:

I'm using asp.net mvc with entity framework and starting to learn DDD. I'm working on project that contains surveys. Here's my domain model:

public class Survey
{
    public int? SurveyID { get; set; }
    public string Name { get; set; }
    public decimal MinAcceptanceScore { get; set; }
    public int UserFailsCount { get; set; }

    public IEnumerable<SurveyQuestion> Questions { get; set; }
    public IEnumerable<Prize> Prizes { get; set; }
    public IEnumerable<SurveyAttempt> UserAttempts { get; set; }
}

I need different parts of surveys for different views so I've created different ViewModels:

    public class ShortSurveyViewModel
    {
        public int? SurveyID { get; set; }
        public string Name { get; set; }
        public int UserFailsCount { get; set; }
        public IEnumerable<SurveyAttempt> UserAttempts { get; set; }
    }

    public class ShortSurveyWithPrizesViewModel
    {
        public int? SurveyID { get; set; }
        public string Name { get; set; }
        public int UserFailsCount { get; set; }
        public IEnumerable<SurveyAttempt> UserAttempts { get; set; }
        public IEnumerable<Prize> Prizes { get; set; }
    }

    public class SurveyEditViewModel
    {
        public int? SurveyID { get; set; }
        public string Name { get; set; }
        public decimal MinAcceptanceScore { get; set; }
        public int UserFailsCount { get; set; }

        public IEnumerable<SurveyQuestion> Questions { get; set; }
        public IEnumerable<Prize> Prizes { get; set; }
    }

What would be the best way to build my architecture if I want my survey repository to get information needed for appropriete view model?

Different solusions that I see:

  1. Repository could return IQueryable to SurveyService and service could return the appropriete view model, but I hesitate that doing this is right because I think view models should be created in UI, not Service layer.

  2. Create three appropriate classes in my domain layer. But now domain will be dependent from representation and with each new view new domain class should be created.

  3. Retrieve full domain object and map just properties that needed for particular view. This is not good because in my example Questions only needed in one representation and it could be heavy collection.

回答1:

Domain driven design:

  • You should have a repository returning aggregate root - in your case Survey and all relations which cannot exist without parent Survey
  • This repository will load always whole Survey class and depending on your requirements just some relations (the really dogmatic DDD would always load whole aggregate but that is not a good approach for stateless web).
  • Your application layer (controller) will ask repository for Survey and selected relations and fills view models.

Onion architecture:

  • You will create some repository exposing IQueryable<Survey> - even worse you will use generic repository with CRUD interface
  • You will create some service calling repository and building Linq-to-entities projection into your DTOs and returning them to application layer (controller)
  • Now what? You can either use those DTOs directly or use another set of objects used as your view models with some UI related attributes etc. There is obviously something wrong ...

Simple architecture:

  • You will use injected IDbSet<Survey> directly in your controller as a repository
  • You will make Linq-to-entities projections directly in your controller to fill view models

There is no best way. It is always about your goal and about your expectations. For small applications you can live with simple architecture without any issue.

Domain driven design is more complex. The main concept in DDD are domain entities, value objects and their composition. Domain entity encapsulates data and logic executed on those data. DDD does not work with partial data or DTOs - when your domain entities don't have any logic you are doing it wrong (it is called anemic model). The service in DDD is not mediator between application layer and repository. It is used to handle business logic which is not related to single domain entity (so cannot be encapsulate in domain entity). Repository is infrastructure code needed to materialize your aggregates from storage and to persist them in storage. Application logic (controller) can interact with domain entities, services and infrastructure code.

I don't like onion architecture.



回答2:

Considering the fact that your primary concern is the amount of data retrieved from the Data Model vs. the amount of data necessary in the View Model, I would say the right approach for you would be to build Views in the database and their respective Data Models.

This would allow you to prune the amount of data leveraged from the database on any one trip.

Though the Data Models would nearly mimic the View Models, that's not a problem, they serve two distinct purposes. The View Model is for the View to bind to - the Data Model knows how to get and save data. Building these views would allow you to get data in the optimal way - and yet house custom save logic in those same models to make them writable if necessary - thus a need arises for the Data Model past moving data from one tier to the next.