Search |
||
Improve JSF by Decoupling Form Rules from Presentation ComponentsPosted by johnreynolds on July 6, 2004 at 8:24 PM PDT
The conceptual goal of this article is to convince component developers that the rules that govern form validation should be embedded in the form definition rather then spread between business and presentation logic. This approach (which I have advocated before) simplifies the mapping between requirements and implementation, and leads to code that is easier to maintain. Forms are first class citizens in the world of Business. Almost all Business Processes begin with the submittal of a Form, and seldom can a Business Process proceed until the Form's input has been validated against a set of rules. Unfortunately, in the world of User Interface Components the Form has lost its first class status. The term Form has been usurped to refer to a set of Data Fields that are presented to a user on a single screen rather then to an underlying Business Object. Consider a web-based application for preparing US Income Taxes: Several screens may be necessary to complete a 1040 Income Tax Form, but from the developer's perspective each screen will submit a unique HTML Form. To avoid confusion, for the remainder of this article I will use the term "Business Form".
Most UI Component Frameworks are Data Field oriented. Controls such as text boxes and drop-down lists are added to screens to gather information from a user. To insure that the user supplies correct information, the controls either constrain the user's input (as with a drop-down list), or the input data is validated against a set of rules for the individual field, or a combination of both techniques is used. Each field has its own validation rules, without respect to the field's context within an underlying Business Form.
In the web world, validation of data fields is often a two stage affair. It is always best to provide feedback to a user's input as quickly as possible, so often input data is validated on the client side (this also has the advantage of reducing round-trips to the server to validate data). Data validation must always be performed on the server side for a number of reasons. Client-side validation cannot be trusted (JavaScript may be turned off, or the HTML form may be "spoofed"), and some validations require knowledge that isn't available on the client (such as verifying that a Zip Code matches a City/State combination). Java Server Faces and Form Validation Java Server Faces is a relatively new UI Component Framework for developing web-based Java applications. Presentation and Validation logic is encapsulated into reusable Controls to greatly reduce the complexity of building HTML based user interfaces. From its inception, JSF was designed for use with graphical design tools, and already some very promising "Visual Basic-like" development environments have been released. Unfortunately, JSF has perpetuated the Data Field orientation of earlier UI Component Frameworks. In JSF, validation rules are defined on a field-by-field basis. To illustrate this point, below is a simple snippet from a JSP page that incorporates a JSF "input_text" field.
<f:view>
<h:form id="myForm">
<table>
<tr>
<td>Social Security Number:</td>
<td>
<h:input_text id="borrowerSSN"
value="#{loanAppBean.borrowerSSN}"
required="true"
/>
</td>
<td>
<!-- error message if borrowerSSN not supplied -->
<h:messages for="borrowerSSN"/>
</td>
</tr>
<!-- Additional fields deleted for clarity -->
</table>
</h:form>
</f:view>
In the above snippet, the markup indicates that the user is required to supply a "borrowerSSN" (the attribute "required" is set to true). If the user submits the HTML form without supplying a "borrowerSSN", an error message will be displayed next to the input field. Admittedly, this is a straightforward and almost trivial example of field validation, but it serves to illustrate the inherent problems with Data Field oriented validation. In the above example, "borrowerSSN" represents a single data field of a Business Form. Possibly, all of the fields of this Business Form reside on a single web page, but possibly the Business Form requires several pages to complete. Regardless, the Business Rule:"borrowerSSN cannot be null" is embedded on this web page. If the Business Rules change, this web page will have to change. There is not a clean mapping from the Business Requirement to the implementation of the requirement.
For trivial applications, Data Field oriented validation has few drawbacks, but consider the potential problems for applications that rely on external services.
Problems begin when the Lender's Business Rules change; Returning to the previous JSF snippet, assume that "borrowerSSN" is part of the Loan Application, and that the field is "required". Assume that Federal regulations change and it becomes illegal to require a borrower to supply an SSN. The Lender's web service and your web application are now in violation of Federal law, so you'll both have to hustle to change your code. The Lender must update the XML Schema of their web service, and you must now go through all the pages of your application to make the proper changes.
This simple example illustrates why Business Form oriented validation is preferable to Data Field oriented validation. Concentrating the rules for validating a Business Form in a single interface requires a bit more "plumbing", but results in a solution that is easier to maintain then the Data Field oriented approach.
Returning once again to our JSF snippet, consider the following changes:
<f:view>
<h:form id="myForm">
<table>
<tr>
<td>Social Security Number:</td>
<td>
<h:input_text id="borrowerSSN"
value="#{loanAppBean.borrowerSSN}"
<!-- get validation rules from the Form -->
validationRules="#{loanAppBean.borrowerSSNValidationRules}"
<!-- replaces: required="true" -->
/>
</td>
<td>
<!-- error message if borrowerSSN not supplied -->
<h:messages for="borrowerSSN"/>
</td>
</tr>
<!-- Additional fields deleted for clarity -->
</table>
</h:form>
</f:view>
value="#{loanAppBean.borrowerSSN}"
A similar approach could be used to obtain validation rules from a Java Bean. Conceptually, the following line gets the "borrowerSSN" validation rules from the "loanAppBean&" and passes them to the JSF "input_text" component.
validationRules="#{loanAppBean.borrowerSSNValidationRules}"
As I mentioned earlier, this example is more verbose then need be. If a convention is adopted to define Rules in BusinessFormBeans, Java introspection can be used to obtain Values and Rules given a single field identifier. Validation of Inter-Dependent Fields Thus far, I've only covered validation of single fields. Business Forms frequently have Rules that govern dependencies between multiple fields. For example, if a Business Form includes a Start Date and an End Date, there will certainly be a Rule that governs both fields (the Start must be earlier then the End). Validation of this nature is often referred to as "Form Based Validation". JSF does not explicitly handle Form Based Validation. The JSF Validation Model is strictly Component oriented. A single Component can gather multiple data values, but dependencies between Components must be handled by implementing custom Event Handlers.
JSF's hooks for implementing custom Form Based Validation are the ValueChangedListener and the ActionListener classes. A custom ValueChangedListener can be registered on any Component. Whenever the value of the Component is changed, the Listener will be invoked.
With these two building blocks, it's possible to perform any necessary Form Based Validation, but making this work seamlessly with the standard JSF Validation Model requires finesse. Consider the following snippet:
<f:view>
<h:form id="myForm">
<table>
<tr>
<td>Start Date:</td>
<td>
<h:input_text id="startDate"
value="#{loanAppBean.startDate}"
required="true"
/>
</td>
<td>
<!-- error message if startDate not supplied or > endDate -->
<h:messages for="startDate"/>
</td>
</tr>
<tr>
<td>end Date:</td>
<td>
<h:input_text id="endDate"
value="#{loanAppBean.endDate}"
required="true"
/>
</td>
<td>
<!-- error message if endDate not supplied or < startDate -->
<h:messages for="endDate"/>
</td>
</tr>
</table>
</h:form>
</f:view>
The process would be much simpler if a BusinessFormBean (a Java Bean that includes "getter" functions for declarative rules that apply to the form and each of its fields) were registered on the JSF form itself:
<f:view>
<h:form id="myForm" businessFormBean="loanAppBean" >
<table>
<tr>
<td>Start Date:</td>
<td>
<h:input_text id="startDate"
value="#{loanAppBean.startDate}"
required="true"
/>
</td>
<td>
<!-- error message if startDate not supplied or > endDate -->
<h:messages for="startDate"/>
</td>
</tr>
<tr>
<td>end Date:</td>
<td>
<h:input_text id="endDate"
value="#{loanAppBean.endDate}"
required="true"
/>
</td>
<td>
<!-- error message if endDate not supplied or < startDate -->
<h:messages for="endDate"/>
</td>
</tr>
</table>
</h:form>
</f:view>
JSF is not unique I hasten to mention that JSF is not alone in its approach to field validation. The Tapestry Web Component Framework (which I happen to think is great) provides Data Field and Form Based validation support that is philosophically similar to JSF. As with JSF, components can be developed for Tapestry to support Business Form oriented validation as I have suggested in this article. I fervently hope that all Component Frameworks consider adopting the approach that I am advocating in this article. There are a number of trends that seem to be leading us towards the need to standardize a "Java Bean-like" interface for retrieving Business Form Rules:
Simplify the Mapping from Requiements to Implementation Component Frameworks have a lot to offer, but they can be better if they stick to presentation and rely on Business Forms to provide validation rules. I hope that I have demonstrated that JSF could be enhanced to consolidate the Rules that govern Business Forms, and that Rule consolidation simplifies the mapping between requirements and implementation. Toolsets that cleanly seperate the definition of Business Forms (and Business Processes) from the creation of User Interfaces and Page Flows will result in code that is easier to maintain and to reuse. At the very least, I hope I've got a few more folks thinking along these lines...
Update:
There are several threads on Sun's JSF forum relating to this issue:
Update: 11Mar05 - Check out Anders Holmgren's Using Java Annotations to add Validity Constraints to Java Bean Properties. »
Related Topics >>
Patterns Comments
Comments are listed in date ascending order (oldest first)
|
||
|
|