在哪里验证依赖于数据库内容的域模型规则?


10

我正在使用允许管理员定义包含字段的表单的系统。然后,使用定义的表格将数据输入系统。有时,表单是由用户通过GUI填写的,有时,表单是根据另一个系统报告的值来填写的。

对于每个字段,管理员可以定义一个验证规则,以限制该字段的允许值。验证规则可以是“在字段中输入的值必须为True或False”到“在字段中输入的值必须存在于数据库表B的A列中”的任何内容。管理员可以随时更改字段的验证规则。

在这种情况下,您认为最适合验证每个字段正确填写的位置是什么?我目前有两种主要方法:

选项1:在域模型中验证

每个字段对象将包含管理员指定的验证规则。Field对象还将引用IValidator。尝试设置字段的值时,字段会将给定的值和验证规则传递给IValidator。如果给定的值无效,则将在另一个系统的GUI /接口中引发ValidationException并进行适当处理。

优点:

  • 强大的保护功能,防止字段被意外分配违反验证规则的值

缺点:

  • 数据访问层需要能够绕过验证并构造违反当前验证规则的字段。尽管管理员更改了字段的验证规则,但我们仍然需要能够基于旧数据构造Field对象,例如,当渲染多年前填充的表单时。每当我们存储字段时,都可以通过存储当前的验证规则来解决此问题。

  • 在这种设计中,Field模型通过IValidator间接链接到数据访问层/存储库。服务/存储库的领域模型的注入似乎被普遍令人难以接受的

选项2:在服务中验证

尝试确保所有尝试设置字段值的尝试都通过可确保验证规则成立的服务。如果违反了验证规则,则抛出ValidationException。

当然,当创建以前一直保存在数据库中的字段对象时,数据访问层将不会使用服务。

优点:

  • 不违反“不要将服务/存储库注入您的域模型”的思想。

  • 保留字段时,无需保留当前的验证规则。该服务可以简单地查询该字段的当前验证规则;查看历史记录数据时,字段的值不会更改。

缺点:

  • 不能保证应该使用服务设置字段值的所有逻辑实际上都这样做。我认为这是一个主要缺点。似乎要做的就是有人写“ thisField.setValue(thatField.getValue())”,并且可能会违反thisField的验证规则,而没有人明智。当数据访问层将要保留字段时,可以通过确保字段的值与验证规则匹配来缓解这种情况。

我目前更喜欢选项#1,而不是选项#2,主要是因为我将其视为业务逻辑,并认为选项2带来了将不良数据引入系统的更大风险。您更喜欢哪个选项,或者是否有比上述两个选项更适合这种情况的设计?

编辑(验证的复杂性)

目前出现的验证案例相对简单。字段值必须是例如数字,日期,带时间的日期,或者是数据库列中的现有值。但是,我怀疑复杂性会随着时间逐渐增加。例如,验证解决方案的构建必须考虑国际化-诸如Date之类的内容可能会以特定于语言环境的语法输入。

我现在决定继续使用选项#1,尝试注意不要为域模型分配太多职责。那些面临类似情况的人可能还想查看相关问题分层体系结构中的验证和授权以及数据输入验证-在哪里?多少?


创建模型非常适合所有验证。但是,当您要编辑或查看表单时,验证也会开始。对于空的字段或具有无效值的字段(可能在数据库中更改或从Excel加载),将引发异常。预期的解决方案是允许表单在更新时显示,以便管理员可以更正这些字段。
tunmise fasipe 2015年

Answers:


4

验证有多复杂?验证通常需要字段和/或依赖字段进行准确评估的业务规则的组合。

验证越复杂,选项#2越困难且性能越差。

当然,数据层可以在持久性时间调用验证服务。这可能有助于解决由于规则更改而导致数据处于无效状态的奇怪情况。

值得一提的另一项是无需更改质量保证周期即可更改验证规则的功能。但这是另一个线程的主题。

根据上述信息,假设您保持纪律,将验证和持久性分开,那么选项1似乎是最灵活的。


0

也许您缺少一层。不知道您的应用程序的详细信息(必要条件,体系结构等),我会做类似Client(无论它是什么)的操作-> Application Service-> Domain Model

允许应用程序服务层与存储库和包含业务逻辑的域模型进行交互。因此,您可以像以下内容:

FieldUpdateService >> updateField(fieldId, newValue)
  List<FieldRule> rules = this.rulesRepository.findRulesFor(fieldId)
  Field field = this.fieldRepository.find(fieldId)
  try 
    field.updateValue(rules,newValue)
    fieldRepository.save(field)
  catch 
    // manage error

如果您在Field和他的FieldRules之间有关系,并且使用Java中的Hibernate之类的ORM,则只能执行以下操作:

FieldUpdateService >> updateField(fieldId, newValue)
  Field field = this.fieldRepository.find(fieldId)
  try 
    field.updateValue(newValue)
    fieldRepository.save(field)
  catch 
    // manage error

Field >> updateValue(newValue)
  for rule in rules
     if !rule.isValid(newValue)
        throw ValueNotAllowedException
  this.value = newvalue

因为ORM查询并实例化了您要求的对象图。

关于您对某人可以做到这一点的怀疑

thisField.setValue(thatField.getValue())”和thisField的验证规则可能会被违反,而没有人明智。

有人也可以这样写:FieldRule alwaysReturnTrueRule = new FieldRule {isValid(newValue){return true; }} field.updateValue(“ uncheckedValue”,alwaysReturnTrueRule)这是一个晦涩的示例,但是我要说的是,没有什么可以保护您免受错误使用代码的伤害。也许面对面的好文档和交流。我认为没有什么可以防止您对代码的错误使用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.