Saturday, February 28, 2009

Handling complex use case flows

There are applications, that have fairly complex flows in user interface. In application I’m currently working on there are forms which can be visited many times during execution of one business process, each time behaving differently according to context (values collected earlier). The challenge here is how to write code, that will be testable and maintainable and also not too complex?

I will use a simplified example here. Let’s say the application is used to collect information about car parts. Parts can grouped and each group get’s it’s own ID. You can also associate parts with a car, in which they have been mounted. If you decide to enter car information, it is obligatory to also enter information about car body (treated as car part). The business analyst says, he wants application to work following way:

  1. If I want to enter car part information, I choose “Enter car part” from menu. Car part form is shown.
  2. I then enter car part information and I have to provide car part group identifier.
  3. I click OK and return to menu. Now I have two options: “Enter car part” as previously – this will create new car part group, and “Enter additional car part” – this will add a car part to previously created group (it’s number is a context).
  4. There also is a possibility to choose “Enter car” from the menu. This option also creates new group, but shows car form instead of car part form.
  5. After filling car information, I’m automatically redirected to car part form with car part type set to “car body”. I have to provide group identifier.
  6. If car body number meets certain criteria, I’m redirected to registration certificate form, where I have to provide registration certificate information.
  7. After doing this, I return to the menu and have option to start a new group or add parts to recently created group.

Let’s not go into details why exactly is the UI so awkwardly designed. Sometimes your users think differently than you do. Instead, let’s look at navigation between forms:

Form navigation diagram

Looks pretty simple, but it doesn’t reflect the use case flow at all. The car part form in this simple example can work in 3 modes: entering a car part, entering additional car part and entering car body. This introduces some complexity already, but the tricky part is this: where do you go after completing the car part form? It can be either application menu or registration certificate form depending on car part information and what happened earlier in the flow. Basically what we need here is a state diagram.

Application state diagram 

This state machine represents our business process of collection car part group information. As you can see the transitions of states are a little more complex than navigation between forms. Three of the states use the same form to perform different tasks: Car, Car part and Car body. Transitions marked with light gray are not directly part of business process, but rather initiate new car part group. It now becomes clear where should we go after leaving car part form – this depends on what state are we currently in.

This diagram makes it clear for us how the application should behave. But how to implement this process? First and most obvious thought is to create a bunch of flag variables and implement some if-else statements. While this is OK in simple scenarios, the real world complexity will make such code completely unreadable and hard to maintain. Next you may consider a workflow engine. This allows you to express transition logic in readable way, but in most cases this is too heavyweight for regular applications. And what if you have to implement this on mobile device? No workflow engine there.

Suppose this application uses MVC pattern. We focus on the “C” here. The controller most probably has methods like “OnCarPartEntered” taking in car part information object as parameter. This method saves the information and decides what view to show next and in what mode. This is where your “if-else” would go if you opted for the first solution. In this post I suggest using the state machine pattern to implement the controller (a similar was used in my post on circuit breaker pattern). You have to implement your controller interface for each state separately. Following diagram depicts the situation:

Class diagram  

The context class is an access point to the state machine. It knows all the states and knows which state is the current one. It exposes business process context (group number here) and methods used to switch states through IContext interface. Since it also implements the controller interface, it forwards all method calls to current state. Each application state has it’s own implementation of IMyController. These classes derive from StateBase, which can contain behavior common to all states. Derived classes override this behavior when appropriate. For example the CarBodyState will check the car body number criteria match after leaving car part form. The CarPartState will just return to main menu in this situation. The state classes are also responsible for switching states in reaction to certain actions of the user. Following diagram shows sample state transition:

Interaction during state switching

Before executing this sequence, application is in Car body state and car part form is displayed. After execution, application is in Additional part state and menu form is displayed. The code is quite straightforward, so I won’t be cluttering this post with it :)

DotNetKicks Image

No comments:

Share