Tuesday, August 4, 2009

Validating Operation, Not Entities

Suppose that our system manages a list of customers, which is used by both the accounting and the shipping departments. As we strive to implement validation logic in our application, we are likely to consider the following solution.

We could define a CustomerEntity class, which could implement an IsValid method that would check the integrity of the whole entity. We would then refuse to save a customer for which this test would fail. There are several drawbacks to this approach, though.

First, let's consider a customer is valid only if the total of his pending orders is less that his allowed credit, and if he has paid his last invoices. Checking these rules requires a lot of data to be retrieved from the database, which induces a high cost every time we save a customer.

Also, the user saving a customer may not know how to resolve such a validation error. For example, let's imagine the shipping department is left unable to change a customer's address because of unpaid invoices. Since this concern is outside of the shipping department's concerns, they should be allowed to proceed, reassured in the knowledge that the invoice issues are correctly handled by the accounting department.

The validation strategy we retain is to enforce the rules at the operation level. Only the customer data concerned by a specific operation needs to be validated.

For example, the NewOrder operation would perform the costly check about the payment of the previous invoices, because the business would otherwise refuse the order. An UnpaidInvoices error code could then be returned, knowing that it will be meaningful to the caller.

On the other hand, the ChangeAddress operation would know nothing about this rule, since an address change is always permitted. This operation could then avoid the costly but unrelated validations.

No comments: