如何在域对象和服务中的验证与 UI 层中的验证保持 DRY
我已经寻找了答案,甚至就这个主题提出了几个问题,但还没有真正找到正确的答案。如何向 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/代码隐藏层:
通过电子邮件获取人员< /strong>
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...
}
为域对象中可能需要验证的每个属性创建单独的验证方法是否正确?
域对象和服务中的这些方法应该是静态的,这样我就不必创建 person 的实例来进行验证吗?
我是否应该公开服务中 Person 域对象的验证,以便用户不需要知道在哪里查找它们(因为我将一些放在服务中,一些放在 POCO 中的原因实际上是一个实现问题)?
4.有更好的办法吗?
I have searched for answers and even asked several questions on this subject, but haven't really found the right answer yet. How do I expose validation methods in my POCO Domain objects and services to the UI layer? Currently I am using web forms.
For example, I have a the following domain object:
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;
}
}
and Domain Service:
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.. }
}
and UI / Codebehind layer:
Get person by email
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...
}
Is it correct to create separate validation methods for each property in the domain object that may need validating?
Should those methods in domain object and service be static so I don't have to create the instance of person just to do the validation?
Should I expose the validations from the Person domain object in the Service so the user doesn't need to know where to look for them (as the reason that I have put some in service and some in POCO is really a implementation issue)?
4.Is there a better way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Re #1 - 是的(这是一种有效的方法),假设域对象最适合“知道”正确的输入是什么。
回复 #2 - 是的。
Re #3 - 这样做没有什么坏处,但是,如果您不信任类外部的东西能够/负责实际验证,那么为什么您会信任它来调用验证呢?
我会在设置值时强制进行验证,一旦对象中有“良好的数据”,以后就不需要验证它了。这导致了#4...
Re#4 - 以某种方式提供/公开验证的好处是系统的其他部分可以使用它;典型的示例是在 UI 中,您可以通过在输入或提交时验证输入来提供更好的用户体验。
验证的另一种方法是确定好的数据是什么样子(从整体上看) - 并为其定义一堆作为(单独的)公共域级别“服务”存在的规则。验证每个域对象内的输入是很好的,因为随着时间的推移,各个域对象逐渐成熟,您可以更改特定的规则(您可以限制孤立更改的影响) - 缺点是您将重复很多规则。
通用服务可以解决这个问题,服务会说“这就是有效的电子邮件地址的样子”,您的所有域对象都会遵循该服务来告诉他们什么是好的电子邮件地址。
这种方法的“技巧”是要小心如何命名验证方法 - 不要太含糊或不明确。例如,您可能会发现大多数具有电子邮件属性的域对象都使用一种“主要”电子邮件验证方法
ValidateGenericEmail()
,但您经常会遇到其他对象属于特殊情况的情况具有特殊规则ValidateCorporateEmail()
。没关系,将它们添加到验证服务中,因为这是域层中用于管理这些规则的中心位置。然后,您的域对象仍然可以执行您之前需要它们执行的所有操作 - 只不过您已将规则拉出到单独的公共位置。
Re #1 - Yes (it's a valid approach) on the assumption that the domain object is best placed to "know" what correct input is.
Re #2 - Yes.
Re #3 - No harm in doing so, however, if you don't trust something external to the class to be able / responsible for the actual validation why would you then trust it to call the validation?
I would enforce the validation when the values are set, once you have "good data" in the object there should be no need to validate it later. This leads to point #4...
Re #4 - The bonus in somehow providing / exposing the validation is that other parts of the system can use it; the classic example is at the UI where you can provide a better user experience by validating input as it's entered or submitted.
Another approach to the validation is to determine what good data looks like (in an overall view) - and define a bunch of rules for that that exist as a (separate) common domain level "service". Validating input inside each domain object is good as you can change specific rules as the individual domain objects mature over time (you limit the impact of isolated changes) - the drawback is that you'll repeat a lot of rules.
A common service would fix this, a service would say "this is what a valid email address looks like" any all your domain objects would defer to the service to tell them what a good email address is.
The "trick" with this approach is to be careful how you name the validation methods - don't be too vague or ambiguous. For example, you might find cases where most of your domain objects that have an email property use one "main" email validation method
ValidateGenericEmail()
, but you'll often have cases where other objects are special cases with special rulesValidateCorporateEmail()
. That's fine, add them to the validation service because that's the central place in the domain layer for managing those rules.Your domain object can then still do everything you needed them to do before - except that you've pulled out the rules to a separate common place.
我只会在表示层上保留验证并从中清除域模型。因此,领域模型假设数据已经经过验证(而且它已经经过验证,不是吗?)。或者您还有其他数据来源吗?
它会让你的领域模型更加纯粹,你会看到它的核心。但是,通过使用保护子句检查构造函数参数来在对象创建时强制执行一些约束是不错的做法。
I'd keep validation only on presentation layer and clean domain model from it. So domain model assumes that data has been already validated (and it is validated, isn't it?). Or do you have another source where data can come from?
It would make your domain model much more pure and you would see its core. But it is decent to enforce some constraints at object's creation time by checking constructor arguments using guard clauses.