Как оставаться СУХИМ с проверками в объектах домена и службах по сравнению с проверками на уровне пользовательского интерфейса

#c# #asp.net #validation #architecture #domain-driven-design

#c# #asp.net #проверка #архитектура #дизайн, управляемый доменом

Вопрос:

Я искал ответы и даже задал несколько вопросов по этой теме, но пока не нашел правильного ответа. Как мне предоставить методы проверки в моих объектах и службах домена POCO на уровне пользовательского интерфейса? В настоящее время я использую веб-формы.

Например, у меня есть следующий объект домена:

 class Person
{
    public string Name { get; set; }
    public string Email { get; set; }

    public bool IsValidEmail(string email) {}
    public bool IsValidName(string name) {}

    public bool IsValidPerson()
    {
        if (IsValidEmail(Email) amp;amp; IsValidName(Name)) { return true; }
        return false;
    }
}
  

и служба домена:

 class PersonService
{

    private Person person;
    private PersonRepository pRepo;

    public PersonService()
    {
        person = new Person();
        pRepo = new PersonRepository();
    }

    public AddPerson(Person p)
    {
        if (p.IsValidEmail(p.Email) amp;amp; p.IsValidName(p.Name) amp;amp; !DoesEmailExistInDatabase(p.Email))
        { pRepo.Save(p); }
        else
        { throw new ArgumentException(); }
    }

    public GetPersonByEmail(string email)
    {
        if (person.IsValidEmail(Email))
        { pRepo.GetByEmail(email)); }
        else
        { throw new ArgumentException(); }
    }

    public bool DoesEmailExistInDatabase(string email) { //code if exists.. }
}
  

и уровень пользовательского интерфейса / Codebehind:

Связаться с пользователем по электронной почте

 string emailInput = EmailTextBox.Text;

PersonService pService = new PersonService();
Person p = new Person();

if(p.IsValidEmail(emailInput))
{
    Person myPerson = pService.GetPersonByEmail(emailInput);
}
else
{
    //give user error here...
}
  
  1. Правильно ли создавать отдельные методы проверки для каждого свойства в объекте домена, которое может потребовать проверки?

  2. Должны ли эти методы в объекте домена и службе быть статическими, чтобы мне не приходилось создавать экземпляр person только для выполнения проверки?

  3. Должен ли я предоставлять проверки из объекта домена Person в сервисе, чтобы пользователю не нужно было знать, где их искать (поскольку причина, по которой я поместил некоторые в сервис, а некоторые в POCO, на самом деле является проблемой реализации)?

4.Is есть способ получше?

Ответ №1:

Re # 1 — Да (это допустимый подход), исходя из предположения, что объект домена лучше всего расположен, чтобы «знать», какой правильный ввод.

Повторный # 2 — Да.

Re # 3 — Никакого вреда в этом нет, однако, если вы не доверяете чему-то внешнему по отношению к классу, чтобы иметь возможность / отвечать за фактическую проверку, почему вы тогда доверяете ему вызывать проверку?

Я бы применил проверку при установке значений, как только у вас будут «хорошие данные» в объекте, не должно быть необходимости проверять их позже. Это приводит к пункту # 4…

Re # 4 — Бонус в том, что каким-то образом предоставляется проверка, заключается в том, что другие части системы могут ее использовать; классический пример — пользовательский интерфейс, где вы можете обеспечить лучший пользовательский интерфейс, проверяя ввод по мере его ввода или отправки.

Другой подход к проверке заключается в определении того, как выглядят хорошие данные (в общем виде), и определении для этого набора правил, которые существуют как (отдельный) «сервис» общего уровня домена. Проверка входных данных внутри каждого объекта домена хороша тем, что вы можете изменять определенные правила по мере созревания отдельных объектов домена с течением времени (вы ограничиваете влияние отдельных изменений) — недостатком является то, что вы будете повторять множество правил.

Обычная служба исправила бы это, служба сказала бы «вот как выглядит действительный адрес электронной почты», если все объекты вашего домена передадут службе, чтобы сообщить им, какой хороший адрес электронной почты.

«Хитрость» этого подхода заключается в том, чтобы быть осторожным с тем, как вы называете методы проверки — не будьте слишком расплывчатыми или двусмысленными. Например, вы можете обнаружить случаи, когда большинство объектов вашего домена, имеющих свойство email, используют один «основной» метод проверки электронной почты ValidateGenericEmail() , но у вас часто будут случаи, когда другие объекты являются особыми случаями со специальными правилами ValidateCorporateEmail() . Это нормально, добавьте их в службу проверки, потому что это центральное место на уровне домена для управления этими правилами.

После этого ваш доменный объект все еще может выполнять все, что вам было нужно от него раньше, за исключением того, что вы перенесли правила в отдельное общее место.

Комментарии:

1. спасибо за ваш отзыв. Когда вы говорите «Я бы применил проверку при установке значений, как только у вас будут «хорошие данные» в объекте, не должно быть необходимости проверять их позже». » — В пользовательском интерфейсе я обычно проверяю входные параметры еще до создания объекта, поскольку входные данные могут даже не иметь допустимого типа. Кроме того, у меня есть проверки на уровне сервиса, которые взаимодействуют с репозиторием, потому что им нужно запросить базу данных, которая не разрешена непосредственно из POCO. Моя главная мысль заключалась в том, чтобы предоставить потребителю единственную точку для проверки.

2. @Developr — «В пользовательском интерфейсе я обычно проверяю входные параметры еще до создания объекта», это достаточно справедливо. Обычно я делаю это так, чтобы использовать сами установщики свойств в качестве основной точки проверки, потому что нет другого способа передать информацию (то же самое относится к конструкторам — они немедленно «установили» бы значение, используя общедоступный установщик, запускающий проверку).

3. Я начал добавлять проверку для установщиков, но решил отказаться от этого, поскольку до тех пор, пока объект не будет сохранен, он должен быть в состоянии находиться в недопустимом состоянии. Кроме того, с более сложными объектами выдача исключений установщикам может привести к проблемам, когда элементы должны быть установлены в определенном порядке, чтобы избежать исключений.

Ответ №2:

Я бы сохранил проверку только на уровне представления и очистил от нее модель домена. Итак, модель домена предполагает, что данные уже проверены (и они проверены, не так ли?). Или у вас есть другой источник, откуда могут поступать данные? Это сделало бы вашу модель домена намного более чистой, и вы бы увидели ее ядро. Но неплохо применять некоторые ограничения во время создания объекта, проверяя аргументы конструктора с помощью защитных предложений.

Комментарии:

1. @xelibrion — что делать, если вам нужно взаимодействовать с объектом домена на другой странице, тогда вам придется повторять код проверки снова и снова. По крайней мере, если вы применяете базовые проверки к объекту, вы можете вызывать их из общего места. Таким образом, если правила изменятся позже, вы, скорее всего, сможете просто обновить методы.

2. Как насчет того, чтобы возложить ответственность за проверку на viewmodel? Вы не должны использовать свой доменный объект непосредственно на своих страницах. Вы должны преобразовать его в viewmodel и взаимодействовать с объектом домена через service

3. @xelibrion — что, если я захочу создать новый объект person в моем коде за: Person p = new Person(); , тогда я смогу завершить свойства и вызвать службу. Save(p); — Это неправильно?

4. «Я бы сохранил проверку только на уровне представления и очистил от нее модель домена» Без обид, но разве это не противоречие в терминах? Помимо простых проверок на технически неверный ввод, нет способа проверить плохой ввод для бизнеса без знания домена. Если только у вас нет пользовательского интерфейса, вызывающего службу для проверки.

5. @Developr Да, на мой взгляд, это неправильно. Давайте представим, что вы захотите опубликовать службу WCF, которая будет предоставлять точно такую же функциональность, или переместить ваше приложение в ASP.NET MVC, который предоставляет возможности проверки «из коробки». Затем вам придется дублировать этот код. Я бы передал уже проверенный PersonViewModel (просто DTO, который не имеет знаний о домене) в Service. Метод Save().