Use Contexts to Simplify Your Decision Models

Last time I showed the value of Business Knowledge Models (BKMs), those misunderstood and often-maligned elements in DMN diagrams. In this post I'm going to try to do the same for contexts, another DMN feature that is similarly underappreciated and frequently disparaged by tool vendors that don't support them.

Unlike BKMs, contexts are not DRG elements, meaning distinct shapes in the DRD. Instead, a context is a type of boxed expression, a standard tabular format for the decision logic of a decision or BKM. Contexts provide three essential benefits:

  1. They simplify complex decision models, providing a compromise between models with simple-looking DRDs containing complicated literal expressions and DRDs containing many decisions, each with simple decision logic.
  2. They allow you to model complicated decision logic - even a whole DRD - as a single value expression. This is critical if you want to encapsulate that logic in a BKM.
  3. They allow you to construct structured data out of simple values.
We'll illustrate all of these in this post.

Even though contexts are primarily about managing complexity, we will illustrate their use with something simple, the same example we used last time: calculating the monthly payment for a home mortgage. As before, typically this would be just a small part of some larger end-to-end decision.

When you shop for a mortgage, it is common to see loan products specified as a combination of the annual interest rate, an origination fee called "points", and possibly additional fixed fees. Points are a percentage of the requested loan amount that is paid up front. Typically the points and fees are simply added to the requested amount and paid off over time along with the rest of the loan.

Even something as basic as this can be modeled in a variety of styles, according to the tastes and skills of the modeler. For example, James is a business analyst who likes the DRD to break down the logic into lots of simple decisions. He modeled the mortgage payment logic like this:

Each element in James's model has a very simple value expression but it requires four decisions, plus a BKM payment for the amortization formula. Since this calculation represents only 5-10% of the expected end-to-end decision logic, that projects out to a complete DRD with dozens of elements. That could be a problem, because beyond 20 or so elements in the DRD, the decision model becomes difficult to maintain.

Edson is another member of the team. He's more technical and prefers his logic to get right to the point. He modeled the same scenario in a single literal expression:

Edson's DRD has just a single decision node and no BKMs, but that literal expression has a lot going on. If we were to model the end-to-end logic in this style, the DRD won't have too many elements, but their decision logic likely will be too hard for most other stakeholders to understand... and too hard to debug, even for Edson. Both versions of the logic give the same answer for Monthly Payment - $1427.16 - but they represent an extreme difference in modeling style.

Contexts provide a compromise, a way to reduce the number of decision nodes in the DRD while making their decision logic easy to understand. A context effectively collapses a DRD fragment into a single two-column table representing the entire value expression of a decision or BKM. Each row of the table is called a context entry. The first column is the name of the context entry, a variable used in the context's logic. The second column is its value expression, usually a literal expression but possibly any other boxed expression, including decision table, invocation, or even another context. The last row of the context has no name, just a value expression. That is called the final result box, holding the value of the context as a whole.

Each context entry defines a local variable, meaning it can be referenced by subsequent context entries in the context and by the final result box, but not by expressions outside the context. While they are not outputs of the overall decision logic, context entries allow the final result box logic to be simpler and easier to understand.

With Monthly Payment modeled as a context, the DRD still has just a single decision node, but its logic is now easier to understand.

The context breaks out the logic into five context entries, each with the simple decision logic favored by James. It could have fewer context entries, of course, with slightly more complex literal expressions in some of them. Note how, for example, the first context entry, Requested Amount, is referenced by the value expression of the second context entry, Points, and so on, continuing down to the final result box.

Also note that not all the value expressions are literal expressions. Number 4, payment, is a rarely seen type called a function definition. Normally a function definition is modeled as a BKM, but it can also be modeled as a context entry. Here we use it in place of the BKM in James's DRD. Context entry 5, Monthly Payment Exact, is a boxed invocation calling the function payment described above. It has many digits after the decimal point, so the final result box simply applies the function decimal() to it, rounding to two places. Again the calculation gives the same result, $1427.16.

That illustrates the first benefit of contexts, simultaneously reducing the complexity of both the DRD and the decision logic of each node.

Now consider the scenario we discussed in last month's post: We have an input data table of bank loan products, each specified by Rate, PointsPct, and Fees, and would like to compare their respective Monthly Payments by Lender by iterating over the input table Loan Products. That involves iterating a call to the BKM Table Row to generate each row of the of the output table, which simply lists the lender name and the monthly payment. The latter is a call from Table Row to the BKM Monthly Payment.

In this example both Table Row and Monthly Payment are modeled as contexts, illustrating the second and third benefits of this expression type. Table Row is a context with no final result box. In that case the context output is a structure, with one component per context entry. Its datatype tLenderPayment has two components, Lender (Text) and Payment (Number), and the context entries of Table Row reflect that. In fact, this is the normal way to create structured data one component at a time. Contexts with no final result box are especially useful when working with table data. Here the context entry Payment is modeled as a boxed invocation of the BKM Monthly Payment.

The BKM Monthly Payment is a context with a final result box. It looks the same as in the previous example, except now as a BKM it has parameters: Purchase Price, Down Payment, and Loan. The context entry Payment in the BKM Table Row invokes this BKM.

Executing the model gives the results below:

This example illustrates the second and third benefits of a context.

  1. Since Monthly Payment is a BKM, it must be modeled as a single value expression, not a DRD fragment. Contexts provide a way to reduce any decision logic, no matter how complex, to a single value expression.
  2. Creating the table Mortgage payments by Lender one row at a time uses a BKM modeled as a context with no final result box to construct each row. The BKM Table Row is an example of that.
You might notice that this model is still not a single value expression, since it requires two BKMs in addition to the decision. Could we do it all with just one decision and no BKMs? With a context, we can!

The trick is to turn the BKMs into function definition context entries, like this:

Here the context has two context entries, both modeled as function definitions, plus a final result box that iterates over Loan Products. The context entry payment is just the amortization formula, formerly a BKM. The context entry Table Row is also a function definition. It includes a context entry contextRow modeled as a context with no final result box - so it creates the two-column structure. Table Row also contains context entries not in the output that create local variables to simplify the decision logic. Since we want Table Row to output only the two-column structure, its final result box just points to contextRow. This does the trick; now the whole logic is a single value expression!

Contexts should be a part of every DMN modeler's arsenal of tools. You can learn how to use them in my DMN Method and Style Advanced training. It's unfortunate that some vendors that claim DMN conformance do not support them. Every year in the Revision Task Force they tell us, "Business users don't like the tabular format; they like DRDs." But as in the case of BKMs, these vendors typically support only the decision requirements portion of DMN - the DRDs - and offload the decision logic portion to their proprietary rule language. That violates the spirit of a decision language standard, and it's unnecessary.

If your DMN tool does not support contexts or BKMs, please urge them to do so!