如何在域对象和服务中的验证与UI层中的验证保持干燥

本文关键字:验证 UI 对象 服务 | 更新日期: 2023-09-27 17:57:50

我已经搜索了答案,甚至问了几个关于这个主题的问题,但还没有真正找到正确的答案。如何向UI层公开我的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) && 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) && p.IsValidName(p.Name) && !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.. }
}

和UI/代码隐藏层:

通过电子邮件联系人员

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.有更好的方法吗?

如何在域对象和服务中的验证与UI层中的验证保持干燥

Re#1-是(这是一种有效的方法),假设域对象最适合"知道"什么是正确的输入。

Re#2-是。

Re#3-这样做没有害处,但是,如果你不相信类外部的东西能够/负责实际的验证,为什么你会相信它会调用验证?

我会在设置值时强制验证,一旦对象中有"好数据",以后就不需要验证了。这导致第4点…

Re#4-以某种方式提供/公开验证的好处是系统的其他部分可以使用它;典型的例子是在UI中,您可以通过在输入或提交时验证输入来提供更好的用户体验。

另一种验证方法是确定好的数据(在总体视图中)是什么样子的,并为其定义一组作为(单独的)公共域级别"服务"存在的规则。验证每个域对象内部的输入是很好的,因为随着时间的推移,随着各个域对象的成熟,您可以更改特定的规则(您可以限制孤立更改的影响)-缺点是您会重复很多规则。

一个常见的服务会解决这个问题,一个服务会说"这就是一个有效的电子邮件地址",任何你的域对象都会听从该服务,告诉他们什么是好的电子邮件地址。

这种方法的"诀窍"是要小心命名验证方法——不要太模糊或模棱两可。例如,您可能会发现大多数具有电子邮件属性的域对象使用一种"主"电子邮件验证方法ValidateGenericEmail(),但通常情况下,其他对象是具有特殊规则ValidateCorporateEmail()的特殊对象。没关系,将它们添加到验证服务中,因为这是域层中管理这些规则的中心位置。

然后,你的域对象仍然可以做你以前需要他们做的一切——除了你把规则拉到了一个单独的公共位置。

我只会在表示层上进行验证,并从中清除域模型。因此,域模型假设数据已经过验证(并且已经过验证,不是吗?)。或者您有其他数据来源吗?它会让你的领域模型更加纯粹,你会看到它的核心。但在对象创建时,通过使用保护子句检查构造函数参数来强制执行一些约束是不错的。