Thursday, December 20, 2007

Actions leads to Outcomes

In enterprise solutions it’s impossible to keep logic in one place. We are faced with BPM rules engines, line of business applications with their own schemas, input and output limitations, business rules, UI rules, drinking rules and speed cameras! Although it would be great to have them all in one place, as with red tape in governing organs we cope with adapting our senses to read and adhere to this logic in an easy way. K2 blackpearl makes a few effective attempts at centralizing logic and offering us tools to use logic from elsewhere.

In K2 2003 a user task was a good example where we often had routing logic in the workflow that had to be duplicated in the UI. For workflow routing our UI has to know what options the user had when auctioning a task and then the succeeding rule and all line rules out of the activity had to know about this as well. Maintenance was a nightmare.

In order to centralize routing logic, my team built a complete framework on K2.net 2003 that allowed us to automatically render the user interface for a workflow client event by looking at the available lines out of an activity that hosted the client event (if you are not bothered about the battles we had in 2003 or are a K2 blackpearl only client, skip to ‘In K2 blackpearl” sections).

In K2 2003

Here is the sample of the framework in action:


Logic of the workflow is automatically available in our business entities by purely inheriting from a specific base class (Customer in this example with an associated Customer Approval workflow instance) so accessing the logic defined in the workflow via client UI code looks like this:

Public Sub LookAtCustomerActionsAvailable()

' load a biz entity
Dim cust As Customer = _
RemoteServer.GeneralManager.Load(Of Model.Customer)(1)

' get the workflow context using the framework
RemoteServer.WorkflowManager.PopulateContext(cust)

' get the list of actions from the biz entity
For Each action As Action In cust.ActionsAvailable
Assert.IsNotNull(action, "bollocks test")
Next


' take the first action
cust.ActionToBeTaken = cust.ActionsAvailable(0)

' save the entity and action the workflow using framework

RemoteServerHelper.GeneralManager.SaveEntityObject(cust);

End Sub

Powerful feature, but because it is our own framework extensions on top of K2 2003, it is hard to maintain. For example, all our client events, lines, succeeding rules and line rules have to adhere to our framework requirements, i.e. “what action will lead to what line and what line represents a specific outcome”. We have to add and maintain XML stubs on all these entities, i.e. here is a line telling the framework what action will lead to its outcome:


Now that K2 blackpearl is available with the concept of being able to take a client action that leads to an outcome build into the product, life is much easier.

In K2 blackpearl

Blackpearl defines the concept of a client event in the workflow being aware of what actions can be taken on it. This is defined when creating the client even in the process designer. The wizard will also allow you to define outcomes and link the action that a user can take with a specific outcome based on your own rule set. Once the client receives the task at run-time, the task contains the available actions and the UI can automatically render this.

Here is a sample process:


When I added the client event, I automatically added two actions with two outcomes, North and South. I also asked the client event wizard to create a line for each outcome… not a single line of code needed for this.

Once I’ve started a new process instance and look at the allocated client task, I can see the two actions available: - this picture from the Workspace work list:



Job done! I can link these outcomes to my UI using either the various UI integration events available (InfoPath Client Event and Form Generation Client Event) – zero code required.

Alternatively I could render my own UI using the Client API. This sample code opens a connection to the K2 server, opens a task based on its serial number and then loops the available actions. It then takes the first action on the task in order to complete the client event:

static void LoadActions() {
// SourceCode.Workflow.Client.Connection now implements
// IDisposable...

using (Connection con = new Connection()) {
try {
// open connection and load task
con.Open("localhost");
WorklistItem task = con.OpenWorklistItem("SERIALNUMBER");

// loop the actions
foreach (Action action in task.Actions) {
Console.WriteLine(action.Name);
}

// action task (assume there is at least one)
task.Actions[0].Execute();

}
catch (Exception exception) { throw exception; }
finally { con.Close(); }
}
}

Thanks to the ambitions of the development team at Dynamyx that inspired the creation of the Dynamyx K2 framework that’s made life so much easier for us and our clients. I dedicate this blog entry to them. Rob, Ian, Trent, Kenny, Carlos and JT.

2 comments:

jey said...

Here's a post by me on the same topic : http://srikantha.wordpress.com/2007/11/27/actions-and-outcomes/

Partake7 said...

craving your favourite food, however, don’t have time? no need to fear. food ordering system on the partake7 app and revel in freshly made food with quick shipping.
partake7 gives you a long and particular list of the first-class eating places close to you.