Grails commandObject - 设计最佳实践

时间:2014-08-27 10:18:35

标签: validation grails

我在一开始就道歉,因为这更像是一个设计问题,而不是一个特定的问题所以它可能没有一个简单的答案。无论如何,我正在开发一个非常复杂的代码来处理仓库系统中的事务。事务本身是用户定义的开关,用于确定要在屏幕上输入的字段。我使用命令对象来处理/验证表单。许多验证步骤都很简单,但有些是有点棘手,并且在屏幕字段之间存在交叉依赖关系。例如,我可能会提示用户输入序列引用,但如果这不能唯一地识别纸箱我需要请求一个部分与它一起..我然后点击数据库来验证这些组合..屏幕也可能有一个文件参考供用户输入..这反过来可能与部分/序列交叉引用在屏幕上(通过点击数据库)以确保部件/序列用于文档..这将继续,取决于在事务上定义的内容,具有更多交叉引用和验证。我最终得到的是很多'如果这输入并输入..验证..否则..验证'在命令对象中键入东西,它看起来真的很难看。所以我的问题是:我应该把所有的验证都放在命令对象(所有的数据库检查等)中,还是有更好的地方放置它,有什么我能做的假设这是一个命令对象,那么比我所有复杂的if / else组合更好吗?我曾想过创建当前命令对象可能产生的其他类,使那些可验证并在那些中传播逻辑。如果有人想要芯片我欣赏讨论..

在Joshua的评论之后,我已经开始将代码重构为服务......它还没有完成,但它正在形成..

@Transactional
class TransactionValidationService {

static enum state {
    RECEIPT,
    RECEIPT_SERIAL,
    RECEIPT_DOCUMENT,
    RECEIPT_PART,
    RECEIPT_SERIAL_PART,
    RECEIPT_SERIAL_DOCUMENT,
    RECEIPT_PART_DOCUMENT,
    ISSUE,
    TRANSFER
}

def validateTransaction(TransactionDetailCommand transaction) {

    // Set initial state  ..

    def currentState

    switch (transaction.transactionType.processType) {

        case ProcessType.ISSUE:
            currentState = state.ISSUE
            break

        case ProcessType.RECEIPT:
            currentState = state.RECEIPT
            break

        case ProcessType.TRANSFER:
            currentState = state.TRANSFER

    }

    switch (currentState) {

        case state.RECEIPT:

            if (transaction.serialReference) {
                // validateSerialReference
                currentState = state.RECEIPT_SERIAL
            } else if (transaction.documentHeader) {
                // validateReceiptDocument
                currentState = state.RECEIPT_DOCUMENT
            }

            break

        case state.RECEIPT_SERIAL:

            if (transaction.part) {
                // validatePartSerial
                currentState = state.RECEIPT_SERIAL_PART
            }

            if (transaction.documentHeader) {
                // validateDocumentPart
                currentState = state.RECEIPT_SERIAL_DOCUMENT
            }

            break

        case state.ISSUE:
            break

        case state.TRANSFER:
            break

    }

}

}

1 个答案:

答案 0 :(得分:1)

首先,使用命令对象收集此信息是正确的选择。

然而,在约束中实现复杂的验证逻辑作为自定义验证器可能有点压倒性。您可能需要考虑将服务注入命令对象,然后从自定义验证器中将验证委派给服务。

例如

@Validateable
@ToString(includeNames=true)
class MyExampleCommand {
  def myValidationService = Holders.grailsApplication.mainContext.myValidationService

  String someThing
  Long someValue
  ..
  static constraints = {
    someThing(
      nullable: false, 
      blank: false, 
      size:1..20, 
      validator: { val, obj -> 
        obj.myValidationService.validateSomeThing(obj) 
      }
    )
    ...
  }
  ...
}