Page-Flow vs. Process-Flow – how a UI Mediator might help

April 04 2012 by Bernd Rücker · 7 Comments

Imagine you have a customer registration process. For the user it feels like having 5 forms in a row to enter some data (we call that page-flow). In the background you want to query some scoring-service. Hmm, this should not be done in the UI layer, right? Isn’t that business logic? And what if the scoring service may not deliver an immediate score but just tells you that the result is delivery asynchronously later on (which is a real life example)? This imposes some serious requirements for a persistent process state, maybe even with some escalation if the result doesn’t arrive in the agreed SLA. In this situation process-flow and page-flow somehow intervene, but still must be separated wisely. Today I want to shed some light on this on a conceptional level with BPMN 2.0 and the UI Mediator Pattern but as well present a working prototype using camunda fox, Activiti and Java EE 6 (especially JSF and CDI). So for non-technical people: Hopefully you can still understand the first part. For the technical folks: Be patient, we get down to code in the second part :-)

How the user perceives it

The following BPMN shows how the user takes part in the process. Please note, that the whole blog uses the BPMN methodology we explain in our book on BPMN (unfortunately only German or Spanish at the moment) or in this blog post.

The process from the perspective of the user

The process from the perspective of the user

Note that there are basically two different possible flows for the user, either a score is provided immediately and he can do the whole work at once or the score is provided later and he has to come back later, query his task list and do the last step in the process model from there. To make this more clear, I visualized the flows by adding screen-shots of the application I developed as a prototype:

The user has to come back via the task list when the score is received asynchronously

The user has to come back via the task list when the score is received asynchronously

The user experiences one page-flow when the score is retrieved immediately

The user experiences one page-flow when the score is retrieved immediately

The automated process for the engine

As I said earlier, we have an automated business process for implementing this application. Using a process engine for this task comes handy for two features we can leverage in this case: First we need some persistence if the score is not returned immediately and second we want to add some escalation if the answer is not in time. With an BPMN 2.0 Engine like Activiti, we just have to add an attached boundary timer event to really implement an escalation, awesome.

The following image shows the process model as I deploy it later to Activiti, basically three things are remarkable:

  • The number of UserTasks does not match the number of forms the user sees. This is not uncommon since the granularity of the user forms is basically dependent on the technology, on your smartphone you might have more forms than in a normal browser on your wide screen at work.
  • The form we use to enter the customer data in the beginning does not correspond to an user task. This form is filled before the process instance is started at all (if the user cancels here, no process instance will be started at all).
  • We do user validation within the process engine and have a first user task for entering additional data. This means, that if the users stops here and closes his browser, the process instance will remain in the Activiti database and he can continue later from the task list. I decided to model it like this in the demo, it is not the only possibility, agreed, there would be alternatives, but I consider it not to be unrealistic to first have a “safe point” after validation in this case.
Process automated in Activiti

Process automated in Activiti

How do we get this together?

In order to understand the big picture we need to have a look at the JSF user interface I developed for our small use case, which is included in the following process model. You can see that for example the customer details are “just” two forms which are implemented by a simple page-flow in JSF (page-flows are built-in). But for the other “flow logic” we need to call the process engine.

User, Activiti and the JSF Layer in between

User, Activiti and the JSF Layer in between

But how do we get the synchronous one page-flow feeling for the user for this?

UI-Mediator-Pattern

UI Mediator

UI Mediator Pattern

The UI-Mediator-Pattern basically described for a asynchronous SOA world how to map an asynchronous behavior under the hoods to a synchronous feeling on the user interface. That’s exactly what we want. And since Activiti normally behaves synchronously in terms of threading anyway (for details see the Activiti user guide) it is easy to implement in this environment. This mediator is a generic component we add to our application, which checks after a user interacts with the process engine if some task exists for the same process instance for the current user, depending on the configuration you might even wait a couple of seconds if a task will be created. If yes, the task form it is shown immediately to the user simulating a synchronous page-flow. This can now be easily mixed with real page-flow in JSF.

Done :-)

The demo application

Cool, but how to implement it? Actually it doesn’t take more than the camunda fox platform community edition, which includes a JBoss AS 7, and an evening of time. The whole application can be checked out from our public SVN: https://svn.camunda.com/fox/demo/fox/customer-registration-ui-mediator/trunk/. You just have to build it using Maven and deploy the resulting war on the fox platform and it should work.

Important addition (2012-April-28): I adjusted the code in the showcase to use JMS for the asynchronous response. That works pretty well and shows how to correlate an incoming JMS message to the right process instance. In order to get this running you have to enable the JBoss full profile to get it working! Here you find the standalone.xml you need, just replace the existing one in your JBoss 7 (or you build that yourself, in our fox user guide we have documentation for this, unfortunately this is currently not open for public but only for our enterprise customers).

Implementation details

I don’t want to repeat how to build process application with the camunda fox stack here, we showed that for example in this blog post and recently wrote a JavaMagazin article which is published soon. I want to concentrate on the UI mediation parts of it, where I used some CDI magic. At least it felt like magic ;-)

First I specialize the existing BusinessProcess bean from the Activiti CDI module. Now my own bean is used instead of the original one and I can add additional code which is always executed (a bit like Interceptors). In this bean I only hook in the UiMediator, which is described next.

/**
 * Specializes {@link BusinessProcess} bean to add generic code needed for UiMediator
 */
@Specializes
public class UiMediatedBusinessProcessBean extends BusinessProcess {

  //we do not have a logged in user in the demo app
  private String currentUser = "kermit";

  @Inject
  private UIMediator uiMediator;

  @Override
  public void completeTask() {
    Task task = getTask();
    super.completeTask();

    if (task != null) {
      uiMediator.checkProcessInstanceStatus(task.getAssignee(), task.getProcessInstanceId());
    }
  }

  @Override
  public ProcessInstance startProcessByKey(String key, Map<String, Object> variables) {
    ProcessInstance processInstance = super.startProcessByKey(key, variables);
    uiMediator.checkProcessInstanceStatus(currentUser, processInstance.getProcessInstanceId());
    return processInstance;
  }
}

The UiMediator just does the work I described above: it checks if a task is created for the same user in the same process instance immediately. If yes, it just saves it to some properties, which we can use in the JSF page-flow to route to the correct pages.

@RequestScoped
@Named("uiMediator")
public class UIMediator implements Serializable {

  @Inject
  private TaskService taskService;

  @Inject
  private FormService formService;

  private String nextTaskId;

  private String nextTaskForm;

  /**
   * check for new task in same process for same user
   */
  public void checkProcessInstanceStatus(String assignee, String processInstanceId) {
    nextTaskId = null;
    nextTaskForm = null;
    if (processInstanceId != null && assignee != null) {
      List<Task> tasks = taskService.createTaskQuery().taskAssignee(assignee).processInstanceId(processInstanceId).list();
      if (tasks.size() == 1) {
        nextTaskId = tasks.get(0).getId();
        nextTaskForm = formService.getTaskFormData(nextTaskId).getFormKey();
      }
    }
    // possible extension here: wait a couple of (milli)seconds to check if that task is reached asynchronously if you have this requirement
  }
  public String getNextTaskId() {
    return nextTaskId;
  }
  public String getNextTaskForm() {
    return nextTaskForm;
  }
}

For the page-flow we now add navigation rules, that every time a process instance is started or a task is completed (the later is shown in the code below), we check the UiMediator for a next form. If this is provided we go on this page, otherwise we go back to the task list. The nice thing about this implementation is, that our application does not need any specific code, the whole solution is pretty generic, you only need the correct navigation rule to form a proper page-flow in JSF (maybe if I have another evening of time I will come up with a workaround for this ;-) ).

<navigation-rule>
  <navigation-case>
    <from-action>#{businessProcess.completeTask()}</from-action>
    <if>#{uiMediator.nextTaskId==null}</if>
    <to-view-id>/app/taskList.jsf</to-view-id>
    <redirect/>
  </navigation-case>
  <navigation-case>
    <from-action>#{businessProcess.completeTask()}</from-action>
    <if>#{uiMediator.nextTaskId!=null}</if>
    <to-view-id>/app/#{uiMediator.nextTaskForm}.jsf</to-view-id>
    <redirect>
        <view-param>
            <name>taskId</name>
            <value>#{uiMediator.nextTaskId}</value>
        </view-param>
    </redirect>
  </navigation-case>
</navigation-rule>

That’s it. Not so hard actually, right? A good example of the power of using Java EE 6 together with camunda fox (including BPMN 2.0 and Activiti) for Process Applications :-) Feedback welcome!

Bernd Rücker, CEO

Bernd Rücker is co-founder and CEO of camunda. But he is also software developer, trainer and consultant who was activley contributing to the Open Source Workflow engines jBPM 3 and Activiti before starting camunda BPM. He has profound experience in BPM projects and Java Enterprise. He worked with Process Engines and BPM for over 10 years in countless real-life projects. He is author of several books, numerous articles and regular speaker on conferences.

7 Kommentare zu Page-Flow vs. Process-Flow – how a UI Mediator might help

  1. Danke Bernd!

    Das Thema gehört schon lange an die BPMS Öffentlichkeit. Gerade im Bereich Prozessoptimierung sind Brüche zwischen den Aufgaben mindestens ein großes Ärgernis. Bei vielen BPMS Herstellern ist das UI-Mediator-Pattern nicht bekannt. Man musste teilweise sehr umständlich und lange erklären, was man als Kunde möchte, um dann zu hören, dass es nicht geht. Software AG und IBM mussten in einem PoC einen Workaround basteln. Andere Hersteller schlugen abenteuerliche Verrenkungen der Page-Flow-Tasks und ähnlichen Quatsch vor.

    Weiter so!

  2. Hallo Bernd!

    Sehr schöner Beitrag zu einem interessanten Thema!

    Entweder ich übersehe das was oder du hast in Bild 6 (User, Activiti and the JSF Layer in between) einen kleinen Fehler eingebaut:
    Wenn im Pool “User Customer Registration” eine Instanz durch das Nachrichten-Start-Event erzeugt wird, existiert keine Instanz im Pool “JSF Pageflow” mit der die Aktivität “print and send confirmation letter” kommunizieren kann. (Bei einem Start durch das Event “new customer” erreichen die Instanzen beider Pools immer mehr oder weniger gemeinsam einen Endzustand und sind damit beendet.)

    VG
    Eileen

  3. Hi Eileen.
    Schön das unsere Methodik verstanden und gelebt wird :-) Aus BPMN Sicht gebe ich dir Recht. Allerdings ist der Pool “JSF Pageflow” aktuell nur zur Visualisierung/Dokumentation modelliert und wird nicht direkt umgesetzt, daher habe ich mir diese Freiheit zur Vereinfachung rausgenommen, da es für den darzustellen Sachverhalt recht wenig Relevanz hat.
    Grüße
    Bernd

  4. Hallo Eileen,

    touché, Du hast natürlich recht, das ist nicht so sauber modelliert wie es eigentlich sein sollte. Aber wie Bernd schon schreibt, war das in diesem Fall der pragmatische Kompromiss – letztendlich auch in Ermangelung einer guten Alternative, die uns spontan eingefallen wäre.

    Falls Du hier eine Idee hast, gerne her damit!

    VG Jakob

  5. Interessanter Post!
    Wir haben ein ganz ähnliches Konzept mit JSF1/Seam2 und einer anderen Prozess-Engine umgesetzt. Die Erfahrungen “in der Technik” sind ganz gut – die UI wird damit u.a. auch von Überlast in der Prozessengine entkoppelt. Je nach Berechtigungen können User mit diesem Modell längere oder kürzere Pageflows bekommen und meistens flüssiger arbeiten.

    Allerdings ist es nicht immer so einfach, die Anwender davon zu überzeugen, dass eine Wartezeit auf einen Folgetask tatsächlich ein Feature und kein Performance-Bug ist. Um so flüssiger die Pageflows laufen und um so stärker der (asynchrone) Geschäftprozess in den Hintergrund tritt, desto sensibler werden auch kurze Wartezeiten beanstandet. Schwierigkeiten gibt es auch, wenn bspw. der Scoring-Service auf ein Timeout läuft. Die JSF-UI muss dann lange blockieren, weil ja ein Folgetask kommen könnte. Hier muss man in Schulungen usw. wirklich deutlich machen, dass eine Prozess-UI kein Datenbank-Eingabgeformular ist (was mühsam ist).

    Insofern ein spannendes Thema! Freut mich, dass so ein Feature auch woanders als sinnvoll angesehen wird;)

  6. Hallo Jan. Danke erst mal fürs Feedback! Mit dem Blockieren der GUI hast du natürlich völlig recht und ich sehe eine Alternative: Man benutzt hier irgendwas AJAX-artiges, damit könnte man das warten ja in der GUI so gestalten, das es kein Blockieren ist. Ist natürlich aufwendiger, aber technisch genauso möglich. Letztendlich sollte der Benutzer aber vermutlich so oder so für das Grundproblem der Asynchronität sensibilisiert werden. Ich finde in diesem Zusammenhang auch immer wieder diesen Artikel schön (wenn auch nur grob verwandt mit der aktuellen Diskussion: http://eaipatterns.com/ramblings/18_starbucks.html).

  7. Important addition: I adjusted the code in the showcase to use JMS for the asynchronous response.

    That works pretty well and shows how to correlate an incoming JMS message to the right process instance. In order to get this running you have to enable the JBoss full profile to get it working! Here you find the standalone.xml you need here: https://svn.camunda.com/fox/demo/fox/customer-registration-ui-mediator/trunk/standalone.xml

    Just replace the existing one in your JBoss 7 (or you build that yourself, in our fox user guide we have documentation for this, unfortunately this is currently not open for public but only for our enterprise customers).

    Hopefully that doesn’t cause any trouble with running the example.

Schreiben Sie einen Kommentar

Powered by WP Hashcash