Объекты EF против сервисных моделей и моделей просмотра (MVC)

Я пытаюсь понять и определить хорошие практики для разработки моделей вашего приложения / домена (POCOs/DTOs).

Допустим, у меня есть следующая таблица базы данных, Account:

UserID int
Email varchar(50)
PasswordHash varchar(250)
PasswordSalt varchar(250)

Конечно, EF4 будет строить сущность так:

public class Account
{
    public int UserID { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public string PasswordSalt { get; set; }
}

Теперь, допустим, у меня есть модель представления для регистрации нового пользователя, которая может выглядеть примерно так:

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

Наконец, у меня есть сервис, который должен зарегистрировать пользователя:

public class RegistrationService
{
    public void RegisterUser(??? registration)
    {
        // Do stuff to register user
    }
}

Я пытаюсь выяснить, что передать в метод RegisterUser. Модель представления, конечно, находится под моим веб-приложением (уровень представления), поэтому я не хочу, чтобы это передавалось моему сервису.

Итак, я думаю, одна из четырех возможностей:

1) Установите модель сервиса, которая похожа, если не идентична, на RegistrationViewModel, и используйте это:

public class RegistrationServiceModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegistrationService
{
    public void RegisterUser(RegistrationServiceModel registration)
    {
        // Do stuff to register user
    }
}

2) Настройте интерфейс модели и унаследуйте это в моей модели представления, и настройте мой метод для принятия интерфейса:

public interface IRegistrationModel
{
    string Email;
    string Password;
}

public class RegistrationServiceModel : IRegistrationModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegistrationService
{
    public void RegisterUser(IRegistrationModel registration)
    {
        // Do stuff to register user
    }
}

3) Передайте сущность Account, выполнив сопоставление RegistrationViewModel-to-Account в моем контроллере:

public class RegistrationService
{
    public void RegisterUser(Account account)
    {
        // Do stuff to register user
    }
}

4) Переместите мою модель представления из презентации в слой домена / службы и передайте ее в метод службы:

public class RegistrationService
{
    public void RegisterUser(RegistrationViewModel account)
    {
        // Do stuff to register user
    }
}

Ни один из этих трех сценариев не кажется идеальным, поскольку я вижу проблемы в каждом из них. Поэтому мне интересно, есть ли другой метод, о котором я не могу думать.

Каковы хорошие практики для этого?

Заранее спасибо.

3 ответа

Решение

Вы никогда не передадите модель представления в сервис. Служба даже не знает о существовании модели представления, которую вы могли определить на уровне представления. Сервис работает с моделями доменов.
Используйте Auto mapper для сопоставления между моделью представления и моделью домена и наоборот.

Лично я никогда не слышал о сервисных моделях в DDD (просмотр моделей для сервисов).

Используйте третий вариант, наверняка. Как сказал Шлякер, Сервис должен не знать о презентационной части приложения (частью которой является ваша ViewModel).

Конечно, также не переусердствуйте, включив множество моделей переходов, таких как RegistrationServiceModel или - еще хуже - IRegistrationModel (последний приведет к "взрыву интерфейса" однажды).

Так:

  1. Имейте сущность Домена (сущность POCO, которая сохраняется с Entity Framework или NHibernate или NoRM или любым другим).
  2. Имейте ViewModel, который представляет вашу модель домена в данном контексте. Не стесняйтесь сделать ViewModel за действие контроллера при необходимости. Побочным эффектом строгих ViewModels (те, которые имеют 1:1 с вашим View) является полное отсутствие проблем чрезмерной публикации и недостаточной публикации. Это зависит от вашей конкретной ситуации / вкуса.
  3. Используйте атрибуты DataAnnotation с вашими ViewModels, чтобы обеспечить базовую проверку (не забывайте проверять и бизнес-правила, но они должны находиться за сетью - внутри слоя Services/Repositories).
  4. Никогда не сообщайте Службе приложений о ViewModels. Создайте экземпляр объекта домена и передайте его в службу (для проверки / сохранения).
  5. Используйте AutoMapper в качестве опции для быстрого сопоставления объектов вашего домена с ViewModels.
  6. Карта из входящих ViewModel или же FormCollection к вашей сущности либо в действии контроллера, либо в IModelBinder,
  7. (Необязательно) Я бы порекомендовал следовать принципу грома. Это действительно очень удобное использование ViewModels.

В этом случае имеет смысл использовать DTO (Data Transfer Object). Вы можете создать AccountDto Класс на уровне сервиса и использовать его для передачи регистрационных данных в сервис. В некоторых случаях он может быть похож на ViewModel, но, как правило, вы можете отображать в своем представлении гораздо больше, чем требуется для создания пользователя. Чтобы дополнительно проиллюстрировать это, ваша ViewModel, вероятно, будет выглядеть примерно так:

public class RegistrationViewModel
{
    [Required]
    public string Email { get; set; }

    [Required]
    public string Password { get; set; } 

    [Required]
    [Compare("Password")]
    public string RepeatPassword { get; set; } 
}

В то время как ваш DTO потребует только Email а также Password свойства.

public class AccountDto
{
    public string Email { get; set; }
    public string Password { get; set; }
}

Итак, как вы видите, ViewModel содержит только данные, необходимые для View, Логика проверки электронной почты и сравнения паролей происходит на вашем веб-уровне. Вы используете DTO, чтобы получить только электронную почту и пароль к Сервису. Затем на служебном уровне вы хэшируете пароль, заполняете объект Entity и сохраняете значения в базе данных.

Другие вопросы по тегам