Wednesday 26 July 2017

PCS and Correlations: the next big thing cavemen already used...

You can use BPM, BPEL or Workflow to orchestrate or direct regular processes to get a job done from the beginning through a certain flow with a few decision-points, alternate and parallel flows to the end. A common use that is fine and usefull for most projects. And it can be seen as the driver for software companies to develop process/workflow engines.

However, there are cases that one process spawns off multiple other process instances that are some how related to one particular business case, involved person, or a uniquely distinguishable entity. And often those processes have to interact, with each other. For instance, this year in Split I came up with an idea for a role playing advanture game using chatbots and PCS. Each player would initiate a PCS instance, that when interacting with each other can detect if players meet each other at a certain location.

Correlation Sets are key here...

Right after the acquisition of Collaxa in 2004, in my Oracle Consulting era, I got the chance to be a BPEL trainer for Oracle University, doing several trainings for a few bigger consulting companies in the Netherlands. One of the subjects was about Asynchronous Processes and how the Process Manager used WS-Addressing to correlate the response to the running instance that sent out the request. But together with that Correlation Sets were mentioned. And I did not understand it: why would you need Correlation Sets when the Process Manager handles it all transparently using WS-Addressing? Otherwise put: it was time for me to do some projects.

PCS, BPM Suite and SOA Suite share the same process engine that originated from the early BPEL Process Engine. And as you can detect from my anecdote: Correlation Sets are key in this story. And this functionality is around from the medieval ages. In fact, recently they discovered char-coal drawings in a cave in France, indicating that people form pre-historic times already used Correlation Sets in their BPEL's...

Prototype this...

Let's say you have a customer that is a large company with several responsible participants that are allowed to sign the contract. Some of them are full-authorised signers, while others only have partial authorisation. So either one of the fully authorised participants signs, which would complete the contract, or some of the partial signers have signed that pass the contract over the signing threshold.

For this case we keep it simple: either one of the full authorised participant should have signed or all of the partial signers should have signed. But we'll handle this in a (set of) business rule(s), so it can be made more complex to resemble real-world cases.

The singing process spawns off single signing processes for each participant asynchronously:
This is a quite simple looping, as explained in my previous PCS-Article. This can be made simpler using a multi-instance embedded sub-process:

However, for us, this was a later addition.

This single sign process results in a task for the particular signer. When signed it will respond back to the main signing process, that receives the responses of the actual signers:
For every respondent it checks if it results in the contract being fully signed. If it isn't it will wait for a subsequent sign-response. For the technical interested: since this Receive is coupled to the end activity of the signle-sign process:
it is using the earlier mentioned WS-Addressing to correlate each responding asynchronous sub-process to this particular instance. When one or more signers respond just at the moment the process is busy with determining if it has to wait for another receive, these responses are kept by the process manager and delivered at the moment the process instance activates the receive activity again.

Now the thing is, when the contract is fully signed, there still can be several single sign process active. For each of those a task resides in the task-list of the particular participant, but that does not add to the signing-validity of the contract anymore. Those tasks need to be withdrawn:
In this loop, all the signers are checked and for those that did not sign yet, a Withdraw Signing Event is thrown. Correlated to the single signing instance will cause that process to end, removing the particular task from the participants list.

Let's get into the details...

The list of signers is defined as:



We need to know the signers for a contract.  For each Signer we have some attributes. Two toggles are important here:
  • signed: did the signer sign the contract?
  • fullSignAuthority: is the signer fully authorised to sign the contract? 
I also declared a separate object to contain a list of signers. I don't like list based attributes among single attributes. I want to make it explicit that an attribute contains a list of elements.

The main sign process is initiated with a BusinessCustomerSignEvent:
It contains a reference to the Chamber of Commerce number and the account, opportunity and quote id's in SalesCloud or Configure Price Quote Cloud Services. But also the list of Signers component.

When initiating the Single Sign Process, the BusinessCustomerSingleSignEvent is used:
As shown, its the same as the BusinessCustomerSignEvent exept for the signer: it's a single element here. But to be able to identify the particular signer, we introduced a signerIndex.

Then, lastly, there is the WithdrawSigner event:
Here the signer is not particularly important. The correlating attributes are:
  • accountPartyId
  • opportunityId
  • signerIndex
These correlating attributes are defined by the accountPartyId identifying the customer. For that customer, multiple opportunities can arrise. So actually the contract relates to a particular opportunity. And each signer is identified by a sequence/index.

Implementing the details...

First the Single Sign Process is modelled like:


Referring to the Single Sign Process, defining the correlation starts with defining the correlation key:

This pane is called using the 'shuffle'-icon in the button bar, next to the play button.

The Correlation Key is used to identify a Correlation Set, and can consist of one or more properties. Those properties are then mapped to the correlating activities. You define a key using the plus icon. Then you define one or more properties. Then you assign those to the key, using the shuttle icons in the middle.

The  process starts with the Single Sign event, with the interface defined based on the BusinessCustomerSignEvent :

 But the interesting part are the correlations:

In the properties of the Start activity, click on the Correlations tab.
Since it it's a Start event, the only possible choice we have is to initialize a correlation set.
You need to add a correlation key, the one you defined before.

Then map the properties of the key to attributes of the events arguments.

When the Single Sign proces is initiated, the flow splits in a parallel flow. Both conditional paths are based on the condition that the signed attribute is false. If the signed attribute is true, however unlikely, the process is ended.

As can be seen the flow separates into branches leading to a Sign Contract activity, and a Catch  Withdraw Signer Event  activity. So, both are waiting. The Catch event is based on the WithdrawSigner event and is also based on the correlation key:

Here we have the option to Initialize a correlation key. But, in this case this is not quite sensible. We do want to correlate to the earlier initialized Correlation Set. And here we map the properties to the attributes of the WithdrawSigner event activity argument.

Sharp eyes would notice that the Sign Contract Activity has a boundary event on it:
It also catches a WithdrawSigner event, with a comparable correlation set. Actually, this boundary event could have sufficed for our case. But then the process would be boringly simple... And I wanted to show also a construct that allows for intra-process interactions. Here events are thrown and catched between the branches within an instance.

Flowing through the Single Sign process


So, what happens is that either a signer signs the contract, or the main process throws a WithdrawSigner event.

If the Signer signs, the WithdrawSigner Catch event needs to be released. So, in the main flow after the Sign Contract activity, a throw WithdrawSigner event is done:
 It targets the Withdraw Signer event, from the same process. The flow will look like:


This releases the Catch event in the lower flow-branch.
When this signer was a full signer, the main process flows on to do a withdraw of the not signed signer-tasks:
It would be nice to drill down into the decision functions. They're partly used as Script Tasks. And partly to determine the looping. The Check Signing Status checks if one of the fully authorised signers signed, or if there aren't any partial signers that haven't signed yet.

Maybe the most interesting one is the Determine Signers to Withdraw Tasks for:

If the signer has signed, then the signer is removed from the list. This adds to the list functions mentioned in my previous article. The resulting list is traversed to throw a withdraw event for each of the tasks.

If the main process throws a Withdraw Signing Event for an instance, and the signer has not signed then (which is probably the case), it should throw an event to the cancel the Sign Contract boundary event:
What results in a flow like:


And this will remove the tasks from the lists of all the participants.

Conclusion

This was one of the nicest constructs I made in PCS so far. In the "BPEL pre-history" I found that the Correlation Set concept was a very differentiating feature in the Process Engine market. I could not solve some of the more complex problems in the past without Correlation Sets. Maybe this experience made me very fond of this feature. And it's nice to be able to demonstrate it in PCS.

As said, the Boundary event on the Sign Contract activity would already suffice. It turned out I did not need the complex parallel flow with the extra branch to wait for the Withdraw Signer event. But wouldn't you agree this process looks more interesting? It at least also demonstrates how to release blocking paralallel branches within a process.

No comments :