An often repeated charge against DMN is that it makes iteration - a key feature of real-world decision logic - too difficult for business users. "It's programming!" they wail. "Business users can never understand this!" But actually, it's not so hard.
To iterate a decision in DMN you must start with a list. Iteration simply means performing some bit of decision logic on each item in the list. For example, if you are a bank classifying assets for regulatory compliance, you iterate the classification decision over each asset in the list. If you are an insurance company selling automobile policies, you iterate the eligibility and pricing decisions over each car and driver on the policy. If you are a retailer, you iterate the price calculation over each item in the order. Conceptually, this is fundamental. It is not rocket science.
Most often the input list is actually a table, a list of rows, each row representing an instance of some business object: an asset to be classified, an automobile to be insured, or an order to be priced. The columns of the table represent the attributes of that object. For example, each row of the variable order represents an order item, with attributes like SKU, description, quantity, unit price, and is taxable. Those attributes are specified by the datatype definitions tOrderItem and tOrder, a list of tOrderItem.
Since we're discussing iteration, let's assume we know how to model the decision logic for a single item in the list, for example, the price of a single order item. We'll name that decision item price, a number. It depends on various attributes of order item, plus possibly some other inputs, such as tax rate. In DMN it looks like this:
The decision has 2 inputs, order item and tax rate, and the decision logic is a simple decision table. Nothing difficult so far. OK, now here is the part that the Nervous Nellies think you cannot possibly grasp. The decision order price iterates the item price decision over each order item in the input data order. In the DRD above for item price, the input data order item represents a particular order item value, and item price calculates its price. In the iteration, however, we want the item price logic for any order item. In DMN, we call that decision logic a BKM, a function of two parameters, order item and tax rate. We write it as item price (order item, tax rate), but its logic is exactly the same! It's the decision table shown above. Now the DRD looks like this:
Input data order item has become input data order, a list of order items. The decision item price has become the BKM item price - exactly the same decision table as before - and now the decision Order price iterates the item price logic, once for each order item in order. In other words, for each order item in order, return the item price for that order item. Now we're going to say that in FEEL, so brace yourself! Are you ready? Here it is:
Could you comprehend that? Well then, you must be a programmer!
OK, there is asmall subtlety here that I glossed over. The name of the first parameter of item price is order item; we see that from the decision table. But in the iteration expression above, item price just means "one item in the list, order." We could have named it anything at all. We could have written something like this:
for x in order return item price(x, tax rate)Here the list item is called x, not order item. But notice that in the iteration expression, x is also the first argument (i.e., the value of the first parameter) of item price. In the decision table that defines item price, the first parameter is called order item. This is what links x to order item. If you want to get all nerdy about it, the general syntax for iteration looks like this:
for itemName in listName return functionName(itemName, ...)The name after the keyword "for" is the iterator, meaning an item in the list specified by the name after the keyword "in". After the keyword "return" is typically the name of a BKM, with the iterator referenced in its list of arguments. (The ... means there are possibly additional arguments besides the iterator, and the iterator is not required to be first.)
And that is pretty much all there is to iteration in DMN. Note that the result is another list, in this case a list of item prices. If we want to add them up to get the total order price, we need to sum the items in the list. Best is to use a context, like this:
So, to recap, here is the basic recipe for iteration.
- Since the input list is usually a table, define the table columns in the item datatype, and add a second datatype for the table as a whole.
- Define the decision logic for a single item in the list.
- Express that logic as a function - a BKM - in which the inputs of Step 2 above become the parameters of the the function.
- Define a new decision for the iteration, changing the item input from Step 2 to a list input, and changing the decision from Step 2 to a BKM.
- In the iteration decision, use a literal expression invoking the BKM using the for..in..return syntax. This decision returns a list of values. If you want to convert it to a single value, use a context in which the iteration is a context entry and the final result uses list function like sum() or min().