Any URL that accepts HTTP POSTs should not render an HTML page. It should only perform an action on the web application, such as creating a new order, or updating a customer's record.
Displaying a page at the same time would only cause confusion for the web site's user. I really like this page, but it was displayed because I updated this customer from Chicago. What if I want to redisplay the same page later, will I need to update the customer one more?
And what about the dreaded message box when the refresh button is used: To display the webpage again, Internet Explorer needs to resend the information you've previously submitted. Retry, Cancel?
To avoid these issues, an HTTP POST should be responded to by a redirection inviting the browser to GET another URL. This gives the web site's user a clear address to return to should he like the page displayed as a result of his actions.
This implies that when we configure the routing for a given URL, we really should assign it only one of the HTTP verbs. Either the URL will accept HTTP GETs and display an HTML page, either it will accept HTTP POSTs and perform a redirection.
Saturday, August 8, 2009
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.
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.
Subscribe to:
Comments (Atom)