Sunday, November 22, 2009

A Page Flow Challenge

In this post I setup a page flow challenge to find out if the new or up-and-coming web frameworks handle page flows well.

Why am I doing this? Over the last several years I've seen quite a few blogs and discussions where people talk about how web framework X handles page flows, typically comparing the page flow functionality in framework X with Spring Web Flow. The problem with most of these comparisons is that they focus on just two parts of the page flow puzzle: expressing page flows through some kind of DSL, and reusing page flows as modules throughout the application. There is an important third piece: managing navigation and the associated state. The reason why this is often overlooked is probably because the navigation and state management is completely automatic in Spring Web Flow, so many people don't even realize that it's there.

While attending a session on the Lift framework last week at Devoxx09, this issue came up again. Somebody (not me :) asked whether "Lift has page flow functionality like Spring Web Flow?". The answer was as expected: the Lift developers have a page flow DSL in the works but seem to have missed the navigation and state management part (update: this appears to be incorrect, Lift does have navigation and state management.).

It's not enough to be able to define a page flow, the framework also needs to manage it! To help users and framework developers realize this, I've setup a page flow challenge. The idea is that you develop a simple 3-page page flow in your framework of choice and test whether it can correctly run a somewhat contrived scenario that tests things like POST-REDIRECT-GET to avoid accidental resubmission, double submit protection to avoid intentional resubmission, and correct state management. In essence the page flow should behave correctly whatever the user does and protect the user from potential mistakes.

Let's look at the page flow:



Easy peasy. This could for instance be a flow to submit a payment in an electronic banking application, or a flow to handle checkout in a web shop.

The page flow challenge scenario is as follows (note: I've clarified this scenario in a follow-up post):
  1. Start the page flow
  2. On Page 1, enter "foo" and click Next
    You should end up on Page 2 where "foo" is displayed as input from page 1
  3. On Page 2, enter "baz" and click Next
    You should end up on Page 3 where "foo" and "baz" are displayed as input from the previous pages
  4. Click the browser back button
    Without any browser warnings you should end up on Page 2, where "foo" is still displayed as input from page 1
  5. Right click the Back link on the page and do Open Link in New Window
    The new window should show Page 1 where "foo" is displayed in the input field
  6. In the new window, enter "kit" (replacing "foo") and click Next
    You should end up on Page 2 where "kit" is displayed as input from page 1
  7. In the new window, on Page 2 enter "kat" and click Next
    You should end up on Page 3 where "kit" and "kat" are displayed as input from the previous pages
  8. In the original window, click the browser refresh button
    Without any browser warnings you should see Page 2 again with "foo" as input from page 1
  9. In the original window, enter "bar" and click Next
    You should end up on Page 3 with "foo" and "bar" as input from previous pages
  10. In the original window, click Finish
    You should end up on the start page of the application: the page flow has finished
  11. In the original window, click the browser back button
    You should either receive an error immediately, or when you try to resubmit by clicking Finish again
  12. (For extra bonus points) In the new window, (where you still are on Page 3) click Finish
    You should receive an error of some sort (potentially a automatic page flow restart)
UPDATE: I've introduced a new step 11 in the above scenario, and what used to be step 11 is now step 12.

The only extra rule to keep in mind is that the HTML forms should use POST, to follow proper web design rules.

I've made reference implementations of the page flow challenge in Spring Web Flow 1 and 2. The source code is available in Subversion:

Spring Web Flow 1: https://svn.ervacon.com/public/spring/samples/trunk/pageflowchallenge-swf1/
Spring Web Flow 2: https://svn.ervacon.com/public/spring/samples/trunk/pageflowchallenge-swf2/

If you have Subversion and Maven installed, getting things up and running is as simple as:
$ svn export https://svn.ervacon.com/public/spring/samples/trunk/pageflowchallenge-swf2/ pageflowchallenge-swf2
$ cd pageflowchallenge-swf2
$ mvn jetty:run
You can now point your browser to http://localhost:8080/pageflowchallenge-swf2/ to test things out.

This challenge is solely focussed on page flow functionality, so it's not meant to reflect on the quality of a particular framework in other areas. For instance, SWF1 and SWF2 handle this scenario flawlessly, but probably have a harder time dealing with some other use-cases.

If you implemented the challenge in your web framework of choice, please let me know in the comments! I'm especially interested to see how Wicket, Seam, Lift and GWT fare. If you think this is irrelevant for your web framework because it handles things differently, I'd love to learn why you think this is the case, and how a use-case like this would then be implemented.

8 comments:

  1. I don't know who told you that Lift's Wizard lacks state management or navigation, but they were dead wrong.

    Thanks,

    David

    ReplyDelete
  2. David,

    Maybe I misunderstood the explanation Timothy Perrett gave, or maybe he's not completely up-to-date with that part of Lift. I'll update to post to correct this.

    Anyway, good to hear Lift does indeed do navigation and state management! I'd love to see a Lift version of the Page Flow Challenge.

    Erwin

    ReplyDelete
  3. Erwin,

    Im sorry if i didn't explain properly, but lift-wizard can certainly handle state and Im sorry if i gave you the wrong impression. By now I've already seen your post on the lift mailing list and i'll build you a little example - lift can certainly do this challenge.

    Cheers, Tim

    ReplyDelete
  4. Erwin,

    1) https seems unnecessary for your swf1 and 2 examples. This is only an issue because you are using a self-signed certificate, so paranoid people like me who don't like to make exceptions for unknown sites won't view your demo.

    2) Your description does not mention whether the state in a page should be saved if you fill it out, back up, then move forward again. I know that this is a difficult problem to solve in general, but at least in the simplest case, when a user backs up just to review previous entries and then moves forward again with no changes, it would be nice to maintain the data.

    --
    Jim

    ReplyDelete
  5. Jim,

    1) My personal SVN repos use HTTPS with self-signed certificates, and that's where the source code for the SWF1 and SWF2 implementations are. If you want me to send you the source code by email just ping me at klr8@ervacon.com.

    2) When you back up (either using the back button or using a Back link), it is not required to save the value already filled in a text field. Once an input has been submitted by clicking Next, it should be saved of course.

    Erwin

    ReplyDelete
  6. Erwin,

    It seems to me that your scenario doesn't make sense. You allow the user to "branch off" a new window with separate values but then only one of those can be completed. So is it a separate state or not?

    Guus

    ReplyDelete
  7. Guus,

    More people seem to be confused by this last step.

    Let me try to clarify this a bit. This last part of the scenario tests how the framework protects you against double submission: in step 10 "foo/bar" was submitted as result of the page flow. Submitting the "kit/kat" values in step 11 would be a double submit: the single page flow was used to submit two things.

    Is some cases this might be harmless. In other cases this might be problematic (e.g. suppose this is a hotel room booking flow: you don't want to accidentally book two hotel rooms).

    The branching off in step 5 of the scenario simulates the user evaluating alternatives (e.g. do I want this hotel room or the other one). The main point here is that the two branches should not step on each other toes.

    Erwin

    ReplyDelete
  8. This is a good use case. Spring Webflow is good but seam conversation context is the best.

    ReplyDelete