Demo – Book Order Fulfillment

This demonstration is a comprehensive solution of a fictitious company (Books-2-U) that sells books over the internet. The scenario was chosen not to represent any particular industry but rather to be illustrative of how to use the IBM BPM product to solve problems using a story that should be familiar to all readers.

Most of us will have ordered books over the internet by now. The usual pattern is that we visit a web site, browse or search for books, create an order for the books we wish, provide shipping and billing information and then hit the submit button. Within a few weeks, a box arrives on our doorstep containing the books we ordered.

What this story considers is what happens when an order arrives at our Books-2-U company?

What do we have to do to fulfill our customer's order?

|| |!!

Think through what you might steps you might imagine to be involved in fulfilling orders before you continue. See if what you imagined is close to what we imagined. There isn't necessarily a right or wrong answer.|

When designing a solution, gather your staff together and have them start to describe what happens today. A process capturing tool such as IBM's Blueworks Live might be a good candidate for this step but if that is not available, consider a white board or a generic drawing package such as Microsoft's Visio.

After a short while, we came up with the following:


This is about as simple and understandable as it can get. Much later on we will find that there is more to the story than this, but for now, let us assume this is what we have.

Part I – A simple start

Now we create a Business Process Definition (BPD) called Book Order Fulfillment that looks as follows:

These activities mapped exactly from our thinking of our process. We can easily see the flow of work. First we charge the customer and then we ship the books. That was pretty easy. Our thinking here is that if we now implement the two activities, we will have completed our task.

The next step we need to consider is the data associated with our process. All processes have data associated with them. They have to. It is the difference in data content that will differentiate one instance of a process from another. In our scenario, what data items are we working with?

After some thought, we decided on the following:

A Book


Shipping

Billing


An Order


The last data type (order) is the core. An order is considered to be a list of books desired by the customer plus the details of how they are going to pay and finally to where they want the books delivered.

By having made this list, we can again map this to capabilities of the BPM product. The following BPM Business Object data types were built. Business Objects are data types that are created in the Process Designer.

|| |Book

|Address

|ShippingDetails

| |BillingDetails

|Order

||

Now we can start looking at the implementation of the activities. The first activity we will examine is Charge Customer. This activity is meant to charge an amount against a customer's credit card. We will assume that this is a new implementation and we don't have an existing similar service. Thinking about what such a service needs in order to perform its work, we determine that it needs the amount of money to bill plus the details of where to bill via a credit card. Looking at the data already available to us, we find that we do not have a total amount to charge. Instead we have an order which contains a list of items where each item has an associated price. This means that we would like to calculate the total order amount. However, adding a technical step to perform that task would seem to clutter our process diagram. What we do not want to end up with is the following:

This intermixes clean and high level business steps with technical necessities which is not what we want. A solution to this type of issue is the creation of a sub-process which will encapsulate technical steps while leaving the top level BPD pristine:

A sub-process is a sequence of BPD activities that are contained within the parent BPD but which are not immediately visible. Think of a sub-process as a "box" that owns other activities. Notice in the diagram above that the Charge Customer activity has a small icon in its lower right that indicates that it is a sub-process holder.

Notice that the Charge Customer step was changed from a system task to a sub-process. Drilling down into the sub-process, we can now define the following steps within that sub-process:

The true benefit of this is that we maintain our high level story while at the same time defining the next level down to have more details.

So far we have not defined any variables in our process. Thinking about its nature, we see that all process instances start with the receipt of an Order. We model this by defining an input variable on the process. This variable is called order of type Order.

Now we turn our attention to calculating the total amount to be charged against the customers credit card. The Order contains a list of books the customer wishes to purchase and each book has a price associated with. We define a local variable to the sub-process called totalAmount of type Decimal.

Note that because this is defined as private on the sub-process, it will not be visible on the parent process. This is goodness as it is only needed locally to the sub-process and doesn't dirty our parent process space of variables.

The implementation of the Calculate Total activity will be an in-line script. We could have chosen to create a separate service but since there appears to be no other obvious consumers, we don't see a need for this to be implemented in a re-usable fashion. The script itself is a simple loop over the books in the order.

tw.local.totalAmount = 0; for (var i=0; i<tw.local.order.books.listLength; i++) { tw.local.totalAmount = tw.local.totalAmount + tw.local.order.books[i].price; }

Making a charge against the customer's credit card will be a service. As a current place holder, we will define a general service to achieve that task but first we must decide what its input will be. To make a charge against the customer, we need the customer's billing details as well as the amount to charge.

The service we create is called Charge Card and has the following interface:

For now, its implementation is nothing more than a log that it was called:

In the BPD, the activity called Charge Customer (2) is implemented using the Charge Card general service and the data mappings mapped as follows:

Now we return our attention to the activity called Ship Books. Again, this will be a technical service that will cause the books to be shipped. For now we will create a general service to implement this step. We will call this general service Ship Books. Its interface will look like:

The Ship Books BPD activity will be implemented by the Ship Books general service. The mapping from the variables to the input parameters will look as follows:

We are close to completing the first pass of our initial build. We see that the process needs some initial input in order to run. It is extremely good practice to create some default content for input variables for services and processes. This way we can unit test each one easily. When creating default/test content, always make sure that it looks like test content and couldn't accidentally be used as real content that would cause confusion in the future. At this point, defining some sample data for the order variable will be needed for testing.

We are now able to run our process through Process Designer and look at its result in the inspector. After a run, we should see:

In the WAS console, we should also find the information logged by our stub services:

>>> Charge Card Charge amount: 12.34 --- Billing details Card holder name: John Q Sample Card number: 9999 Card expiry: 2099-09-09

  • Address Street: 123 Anystreet City: Anytown State: AA Zip: 9999 <<< Charge Card >>> Ship Books --- Books Sample - The color of magic, Terry Pratchet --- Shipping details Street: 123 Anystreet City: Anytown State: AA Zip: 9999 <<< Ship Books

This completes our first pass at a basic process.

Part II – Billing problems

Our solution so far seems to work nicely but is about as trivial as it gets. Unfortunately, really simple scenarios rarely reflect real world situations. Let us imagine that we put our process into production and almost immediately we find our first problem. If a customer orders books and they provide in-valid billing information, the situation is not handled. At best, the process terminates and the order is abandoned. We think we can do better than this. If the billing fails, we want to try and re-contact the customer and inform them of the problem. It could be they made a typo when they entered their card number or they temporarily had insufficient funds. These are correctable situations and, if we take new billing information, we can recover the process. This benefits the customer as they will get their desired order and it also benefits us as we get the desired revenue.

When we detect a problem with an attempt to charge the customer's credit card, we will take an alternate path. We will ask one of our clerks to examine the order and contact the customer. This may resolve the issue. It may also result in the order being canceled if we don't hear from the customer or else they wish to explicitly cancel their order.

Our first pass at a design for this looks as follows:

We add an exception handler to our Charge Customer activity. This now implies that if there is a problem with billing, an exception will be thrown indicating this. A new activity called Billing Problem is defined which will handle the billing issue. This will have an outcome indicating whether or not the billing issue was resolved. If it was resolved, then we progress to the shipping of the books, if it wasn't resolved, we perform some action to cancel the order.

With this in mind, let us now turn our attention to what is needed to implement these steps. The first activity we examine is the Billing Problem step. We can see that it will be a human task as a clerk must work upon it. The input to the clerk must be the complete order which contains all the details needed to contact the customer and talk with them about their order. The output will be an indication on whether or not the billing issue was resolved. If we spend time looking further at this design, a thought should strike us … a side effect of resolving the billing issue must include actually charging the customer's credit card but that capability is owned by the Charge Customer activity. In addition, our nice clean top level business process is starting to look a little complex. Maybe there is an alternate design.

Here is a second pass at our design.

In this story, the Charge Customer will either succeed or fail and, if it fails, the order will be canceled. The handling of an invalid card is now owned by the Charge Customer sub-process.

The following shows a possible implementation within the Charge Customer sub-process.

Again we assume that the Billing Problem activity will also charge the customer's card if all is well. There is rarely an absolute correct design for a business process and in many cases, the quality of the design will be subjective. The thought behind this, our final design, is that billing is now contained solely within the billing sub-process. Let us now turn our attention to the implementation of some of these steps.

First we notice that Charge Customer (2) throws an exception. Currently, our implementation does not do that. For testing purposes, we will say that if the account number on the card is 9999 then that will be our indication that the card is invalid. The new Charge Customer (2) implementation now looks like:

Now we get to look at the Billing Problem human task activity. Its input will be the Order and the Total billing amount. Its output will be an indication of whether or not the charge was resolved. An initial pass at the implementation of Billing Problem looks as follows:

There appears to be a lot to this so let us take some time to walk through it. First we reach a coach called Contact Customer. When displayed, it looks as follows:

Here we will contact the customer and discuss new charging information. We can either cancel the order or charge the card. If we select Charge, we attempt to charge the card with the newly entered information. This can again fail and, if it does, we tell the clerk that it failed again. The clerk can again talk with the customer. If the charge succeeds, we tell the clerk that the charge has been made. A variable is set to indicate whether or not the charge has been processed before the end of this human service. The Charge Card step is an instance of a nested service that invokes the Charge Card service we built earlier.

Back in our process diagram, we can now associated the Billing Problem activity with its implementation of Billing Problem human service.

Having completed these steps, go back and re-run the solution and validate that it now does what you expect.

Do you see a problem with the solution as currently described? It appears that we missed gathering the customer's contact information such as email and phone number. We can go back to the Order data structure and add these missing fields.

The Billing Problem Human Service was also updated to reflect the new fields.

Part III – Starting process instances

So far we have been testing our processes by running them from within Process Designer and using the default values of the variables to be the input to the process. In reality, we will be starting these instances as a result of orders submitted through the Internet. In our story, we also are able to receive orders that are called in to us via the phone. This means that we need to create a mechanism where clerks can enter the orders manually. We will start by building a human service that allows us to enter the details of an order. We will call this human service Order Entry.

The Order Entry human service must do three things for us. It must allow us to select the books the customer wants, it must allow us to enter the shipping information and it must allow us to enter the billing information. Once this information has been entered, it must be able to start an instance of the process.

A first pass at the screens in the solution looks as follows:

In this screen, the clerk is talking to the customer who is providing titles of books to order. The clerk searches for these by entering the title in the search box and clicking Search. A list of titles that match the search is shown. These can be added to the order. When the desired books have been entered, the clerk clicks Next to move to the next page which is the Shipping coach.

Here the clerk captures the shipping details of the customer. When done, Next is selected which takes us to the Billing coach.

Finally the billing information is requested. This is the last step before the order is ready. Once done, the clerk clicks Order and an instance of the process is started. The Human Service called Order Entry that accomplishes these steps is shown next:

Let us spend some time on this. Notice the three coaches colored in yellow. These correspond to the three previous screens. The primary component of interest to us now is the one labeled Start Process. This is an Invoke UCA step. By the time it is reached, an Order business object has been constructed and we have all we need to start an instance of the process.

The process itself has to be augmented to allow it to be started by the corresponding UCA. The top level Book Order Fulfillment process now looks like:

By flagging the Order Entry as Exposed to be a startable service, we can now launch the Order Entry from the Process Portal.

In the shipping and billing steps, we had the opportunity to enter an address in both screens. Rather than re-code the interface twice, this became a good opportunity to create a reusable visual called a Coach View. We created a Coach View that was called Address that looked as follows:

Part IV – Check Stock

As our business carries on, we find that we are not managing the stock of books on hand very well. Specifically, when a customer orders a book and we don't have it in stock, we don't know quickly enough that we have to order more from our supplier. In addition, when the new supplies arrive, we are slow in fulfilling the order. To this end, we now introduce a new component in our solution that we call Compile Order. This step will determine that we have the books on hand to fulfill the order and, if not, order more stock from the supplier. We will only exit the Compile Order step when we have all the books necessary to ship. Looking at our new BPD below, we see the location of the Compile Order sub-process.

The refinement of the Compile Order sub-process looks as follows:

Here we take the list of books that the customer desires and compare those to the inventory of books on hand. If we have stock of all of our books, we have completed our step. In the event that we are missing some books from the order, then we execute the Order Books step which will contact our supplier requesting the books be sent to us. This is an asynchronous step. It could take some time for the supplier to provide us our books. Because of this, we must wait for the books to be delivered. Here we use a Message Event to achieve that task. A Message Event is an event indication sent to us by some other activity in the BPM system. That message will be generated by an un-specified separate process that is handling the inventory of books received from suppliers.

Let us not forget that there could be many parallel instances of these processes in existence. There could be multiple separate orders for different customers all waiting on different books from different suppliers. Because of this we need to ensure that the correct instance of the process is informed when books associated with it become available and not when books for a different order become known. To achieve this, we introduce the concept of an OrderID. An OrderID is a unique identifier for each order that can be used to distinguish one order from another. We will assume that the OrderID has been generated when the initial order was provided by the customer. We will add this as a new property of the Order data structure which now looks as follows:

The Determine Stock step will again be a technical step. This will compare the current order with available in-house stock and build a new list of out-of-stock books. For our purposes, we will again stub this out. We will simply say that any book that has a title beginning with "Z" is considered out of stock. We will implement Determine Stock as a general service. The interface to the Determine Stock service will look as follows:

The Order Books step will execute a request to order books from the supplier. The inputs to this step look as follows:

Again we will implement this service as a stub merely logging the fact that we are requesting books.

The Books Arrived step is interesting. It blocks the process until a corresponding message has been delivered. The message will come from a UCA. We will match the incoming message content's orderId value to the one contained in the process. If the matching message arrives, we now have the books in stock and can proceed.

A UCA called Books Arrived was built to be associated with this message event.

Part V – Advanced UI

<TBD>

Part VI – Business Monitoring

<TBD>

Page 3

No Comments
Back to top