There are several different ways to look at what constitutes a model for Struts. The lines between business and presentation objects can get quite blurry when dealing with web applications—one application's business objects are another's data transfer objects (DTOs).
It's important to keep the business objects separate from the presentation so that the application is not tightly coupled to one type of presentation. It's likely that the look and feel of a web site will change over time. Studies show that the freshness of a web site's appearance helps to attract new customers and keep existing customers coming back. This may not be as true in the business-to-business (B2B) world, but it's definitely true for business-to-consumer (B2C) applications, which make up the majority of the web applications used today.
The type of model components that you use might also depend on whether you're building a traditional two-tier application or a multi-tiered distributed application. Typically, with a two-tiered application, the business objects are collocated with the web application. Collocation means that objects are deployed within the same JVM. This makes it easier to use these business objects to retrieve data for the views. However, just because it's easier doesn't necessarily make this a smart thing to do. The business objects may be made up of deep object graphs and may contain references to many other nonpresentation resources. If you're not careful, the business objects quickly can become coupled to a specific presentation, which can have unintended side effects each time the look and feel of the web site changes.
One benefit of separating your business objects from the presentation is that you can build coarse-grained objects that your JSP pages and custom tags will have an easier time dealing with. All of your business logic should remain separate from the presentation, and the presentation views should simply retrieve data from the DTO and display it.
The LoginAction class shown in Example 3-3 didn't contain the actual authentication logic. Because the Action class is part of the controller functionality, it delegates the handling of the business logic to another service. In the case of the LoginAction, it relies on a SecurityService component. The SecurityService component may be a session EJB, or just a wrapper around some JDBC code that performs the authentication. In either case, the LoginAction doesn't know or care how the service is implemented. This is helpful, because even if the implementation changes drastically, no code will have to change as long as the IAuthentication interface remains unchanged and is implemented by the service.
This approach also helps with reuse. Say you have another type of client, such as a Swing GUI, that needs to be authenticated. Because the logic is encapsulated in a separate component and not in the Action class, you are free to reuse this security service.
You should strive to keep business logic out of the Action classes to protect against change. In the case of the LoginAction, the login( ) method returns an object of class com.oreilly.struts.banking.view.UserView. This is an example of a DTO. Example 3-4 shows the UserView used in the example application.
package com.oreilly.struts.banking.view;
import java.util.Set;
import java.util.HashSet;
/**
* A value object that wraps all of the user's security information
*/
public class UserView implements java.io.Serializable {
private String id;
private String lastName;
private String firstName;
// A unique collection of permission String objects
private Set permissions = null;
public UserView(String first, String last) {
this(first, last, new HashSet( ));
}
public UserView(String first, String last, Set userPermissions) {
super( );
firstName = first;
lastName = last;
permissions = userPermissions;
}
public boolean containsPermission(String permissionName) {
return permissions.contains(permissionName);
}
public String getLastName( ) {
return lastName;
}
public void setLastName(String name) {
lastName = name;
}
public String getFirstName( ) {
return firstName;
}
public void setFirstName(String name) {
firstName = name;
}
public String getId( ) {
return id;
}
public void setId(String id) {
this.id = id;
}
}
The UserView provides a coarse-grained view of a remote object. There might be five security tables, all joined by foreign keys that contain the data, but when the web tier gets a UserView, it already has been consolidated and made easier to access. In fact, one implementation of this application could get the data from a relational database and another from an LDAP instance. The nice thing about encapsulating the authentication behind the security service is that the presentation tier does not have to change if the security realm is switched. The Action is free to put the UserView object in the request or session and then forward it to a JSP, where the data can be extracted and presented to the user.
The Struts framework doesn't have a great deal of support in the way of model components. This is better left for EJB, CORBA, or some other type of component framework. You can access a database directly from the framework, but you still should attempt to separate that layer from all other parts of the framework. You can do this by making use of the appropriate design patterns to encapsulate the behavior.