You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by Nick77 <ni...@gmail.com> on 2006/11/16 18:23:11 UTC

Best Practice Suggestions

Hi,

I've been playing around with JSF recently, and having had some initial
problems with session-scoped beans, browser back button use and Javascript
submits, I'm posting some ideas for JSF Best Practices...

Separation of Model, View and Controller:
The Model is the 'backing' bean, 1 bean per JSP page, I'll call it the
ModelBean. Contains only the data that is required for display in the view,
and also id fields (the kind of things you'd put in a hidden input in
Struts). Also may contain presentation-specific logic e.g. calculating the
value to use for a rendered attribute, to keep lengthy EL expressions out of
the JSP.

The View is the JSP page (including the JSF component tree). Contains EL
expressions to 'bind' to ModelBean properties, and EL expressions to wire
actions and actionListeners to Controller methods.

The Controller is a separate bean, probably 1 bean per JSP page (maybe not
if you've got some common functionality in different pages). I'll call this
the ControllerBean. The ControllerBean handles events fired by View
components, e.g. action="#{controllerBean.updateCustomerDetails}" and then
calls the appropriate method in the Business Interface. The ControllerBean
has references to both the ModelBean and the Business Interface
implementation which are managed i.e. defined in faces-config.xml. Therefore
a typical user action such as clicking Update Customer Details will invoke
controllerBean.updateCustomerDetails, which will in turn extract any
user-entered data from the ModelBean, use this data as parameters to the
call to the Business Interface, and then depending on the result of that
call, populate the ModelBean with the results and invoke the desired
navigation by returning an appropriate String.

Minimal Bean Scope:
The ModelBean and ControllerBean are in request scope, the Business
Interface implementation is also a managed bean, but is in session scope.

Use t:saveState:
As everything is stateless, how do I ensure that the view has some notion of
the 'current' Customer? Also how do I handle use of the browser back button?
The answer is to use t:saveState to serialise the ModelBean along with the
component tree. That way, when submitting a page (e.g. Update Customer
Details) the ModelBean is not recreated, just de-serialized automatically by
t:saveState. It has non-null values for id, child Collections etc so no
nasty problems with dataTables and commandLinks.

Get rid of Javascript submit():
I wanted dependent drop-down lists, e.g. 2 list boxes, when you select from
the first one e.g. Shape/Colour, it updates the available choices in the
second one e.g. Square/Circle or Red/Green/Blue. Initially I used
onchange="submit()" so I could refresh the page with the new choices in the
second drop-down list. But I found this caused a problem if you use the
browser back button - go 'back' to this page then cause an onchange event -
in my implementation this caused the browser to go 'forward' in its history
cache. This was not the desired effect. So I used the method described 
http://http://wiki.apache.org/myfaces/JavascriptWithJavaServerFaces here  to
avoid the plain submit().


Advantages of the above:
Clear separation of concerns in the presentation layer (instead of combining
Model and Controller like most JSF examples I've seen).
Less use of session scope means less memory required on server and less to
worry about for session failover.
No problems with use of browser back button (assuming your Biz Interface can
deal with 'duplicate' invocations).

Disadvantages:
Increased processing to create/recreate request-scoped beans.
Increased processing, bandwidth and client memory required to use
t:saveState.


This stuff may well be obvious to most people viewing this forum, so if you
think its obvious/rubbish/useful please let me know (and preferably why :-)
all comments/feedback welcome...

Thanks

Nick
-- 
View this message in context: http://www.nabble.com/Best-Practice-Suggestions-tf2644584.html#a7382547
Sent from the MyFaces - Users mailing list archive at Nabble.com.


Re: Best Practice Suggestions

Posted by Craig McClanahan <cr...@apache.org>.
Some thoughts intermixed below.

On 11/16/06, Nick77 <ni...@gmail.com> wrote:
>
>
> Separation of Model, View and Controller:
> The Model is the 'backing' bean, 1 bean per JSP page, I'll call it the
> ModelBean. Contains only the data that is required for display in the
> view,
> and also id fields (the kind of things you'd put in a hidden input in
> Struts). Also may contain presentation-specific logic e.g. calculating the
> value to use for a rendered attribute, to keep lengthy EL expressions out
> of
> the JSP.


Based on my experiences, I have a slightly different viewpoint ... I
consider the backing bean that is associated with the page (JSP, facelets,
clay, whatever) to be part of the view tier, not part of the model.  It's
role is to react to user input events (submit buttons, command links, value
changes), and make presentation-related decisions (is this user authorized
to see this data?  where should we navigate next?).

I like to model the model :-) as separate beans, independent of web tier
APIs, that are used as properties of the backing beans.  For example, if
you're using Hibernate or JPA, it's quite natural to have, say, a Customer
class representing a particular customer.  In a CRUD app, you can make a
Customer property available in your backing bean, and then bind directly to
its properties ("#{backing.customer.name}").

There are a couple of cases where model tier stuff can leak in to the
backing bean (keeping primary keys, embedding simple business logic in
action methods, etc.) but you are generally best off keeping that kind of
stuff separate.

The View is the JSP page (including the JSF component tree). Contains EL
> expressions to 'bind' to ModelBean properties, and EL expressions to wire
> actions and actionListeners to Controller methods.
>
> The Controller is a separate bean, probably 1 bean per JSP page (maybe not
> if you've got some common functionality in different pages). I'll call
> this
> the ControllerBean. The ControllerBean handles events fired by View
> components, e.g. action="#{controllerBean.updateCustomerDetails}" and then
> calls the appropriate method in the Business Interface. The ControllerBean
> has references to both the ModelBean and the Business Interface
> implementation which are managed i.e. defined in faces-config.xml.
> Therefore
> a typical user action such as clicking Update Customer Details will invoke
> controllerBean.updateCustomerDetails, which will in turn extract any
> user-entered data from the ModelBean, use this data as parameters to the
> call to the Business Interface, and then depending on the result of that
> call, populate the ModelBean with the results and invoke the desired
> navigation by returning an appropriate String.


JSF generally encourages a "view helper" pattern, where there is some
backing object per view that serves the controller role.  Especially if you
are using something like Shale, you can get away with using the same backing
bean you are already using for the view events.

I agree with you that segregating the business logic (updateCustomerDetails)
into separate classes from your backing beans is a good idea ... but I think
of that logic as part of the model, not part of the controller.

Minimal Bean Scope:
> The ModelBean and ControllerBean are in request scope, the Business
> Interface implementation is also a managed bean, but is in session scope.


I've seen scenarios where the business interface implementation can be in
application scope ... that works best if it is stateless, and can
legitimately be shared across all users.  Session scope makes more sense if
you need to keep per-user state, and/or different business logic for
different users.

Use t:saveState:
> As everything is stateless, how do I ensure that the view has some notion
> of
> the 'current' Customer? Also how do I handle use of the browser back
> button?
> The answer is to use t:saveState to serialise the ModelBean along with the
> component tree. That way, when submitting a page (e.g. Update Customer
> Details) the ModelBean is not recreated, just de-serialized automatically
> by
> t:saveState. It has non-null values for id, child Collections etc so no
> nasty problems with dataTables and commandLinks.


Here is a place where I don't really like the MyFaces solution ... why
should the author of the view (JSP/Facelets/Clay) need to be concerned with
controller issues like saving state?  That should be in the purview of the
person writing the backing bean.

An elegant way to do that, which has some helper method in Shale's view
controllers, is to leverage the fact that JSF components can take arbitrary
attributes.  Thus, in my backing bean, if I want to keep track of the
customer id I'm currently on, I do something like this (in a prerender()
method if using Shale):

    UIViewRoot root = FacesContext.getCurrentInstance().getViewRootl();
    root.getAttributes().put("customerId", customerId);

and then pull it back out on the subsequent postback.  That way, the person
writing the view doesn't have to be bothered with this kind of thing.


Get rid of Javascript submit():
> I wanted dependent drop-down lists, e.g. 2 list boxes, when you select
> from
> the first one e.g. Shape/Colour, it updates the available choices in the
> second one e.g. Square/Circle or Red/Green/Blue. Initially I used
> onchange="submit()" so I could refresh the page with the new choices in
> the
> second drop-down list. But I found this caused a problem if you use the
> browser back button - go 'back' to this page then cause an onchange event
> -
> in my implementation this caused the browser to go 'forward' in its
> history
> cache. This was not the desired effect. So I used the method described
> http://http://wiki.apache.org/myfaces/JavascriptWithJavaServerFaceshere  to
> avoid the plain submit().
>
>
> Advantages of the above:
> Clear separation of concerns in the presentation layer (instead of
> combining
> Model and Controller like most JSF examples I've seen).
> Less use of session scope means less memory required on server and less to
> worry about for session failover.
> No problems with use of browser back button (assuming your Biz Interface
> can
> deal with 'duplicate' invocations).
>
> Disadvantages:
> Increased processing to create/recreate request-scoped beans.
> Increased processing, bandwidth and client memory required to use
> t:saveState.
>
>
> This stuff may well be obvious to most people viewing this forum, so if
> you
> think its obvious/rubbish/useful please let me know (and preferably why
> :-)
> all comments/feedback welcome...


Craig

Thanks
>
> Nick
> --
> View this message in context:
> http://www.nabble.com/Best-Practice-Suggestions-tf2644584.html#a7382547
> Sent from the MyFaces - Users mailing list archive at Nabble.com.
>
>