DEV Community

InterSystems Developer for InterSystems

Posted on Originally published at community.intersystems.com

Business Rules Deep Dive: Dynamic Objects and Property Access Pitfalls - Part (1)

Hi, Community!

Business Rules in IRIS Interoperability are one of those features that work smoothly until they suddenly do not anymore. You build a rule, the condition looks correct, the routing target is right, but the message keeps falling through to the default action. There is no error in the Event Log and no obvious clue in the Visual Trace.

One of the most common causes is a dynamic object whose properties are not accessible the way you expect. This two-part series covers the full picture. Part 1 walks us through setting up a production using the HL7 Messaging type and building a BPL Business Process that enriches an incoming HL7 message with a dynamic object. Part 2 uses the same production and demonstrates seven real-world property-access pitfalls that cause silent failures in production integrations.


TL;DR

  • A %DynamicObject in IRIS is a schema-free object you create on the fly without defining a class.
  • Use it inside a BPL Business Process to carry enrichment data alongside an HL7 message before the routing rule evaluates it.
  • Exploit %Set() to set properties with special characters like underscores and %Get() to read them back.
  • Part 2 of this series covers seven property-access pitfalls that all silently return empty with no error.

Who This Article Is For

This guide is for IRIS Interoperability developers working with BPL Business Processes and HL7 routing who want to understand how to enrich messages with dynamic objects before routing decisions are made.


What Is a Business Rule?

A Business Rule in IRIS Interoperability is a routing engine that evaluates conditions against an incoming message and decides where to send it. Business Rules do not run on their own; they run inside a Business Process.

In this article, all the data preparation work happens in a BPL Business Process before the routing rule fires. The routing rule then evaluates clean, simple values and makes the decision.


What Is a Dynamic Object?

A %DynamicObject in IRIS is a schema-free object. Unlike a regular ObjectScript class where you define properties upfront, a %DynamicObject lets you create any property on the fly at runtime without a class definition. This makes it extremely useful in integration scenarios where you need to carry extra data alongside a message as it moves through  production.

A common real-world pattern is enriching an incoming HL7 message with additional metadata before the routing rule evaluates it. Instead of modifying the original HL7 message class, you create a %DynamicObject, set whatever properties you need, store it in the BPL context, and read from it downstream.

In our example, we read the message type, patient name, and sending application from the incoming HL7 message and store them in a dynamic object. That object then travels with the message through production, and anything downstream can read from it without touching the original HL7 structure.


The Production We Will Build

The production uses standard auto-created components plus one new BPL Business Process:

  • HL7FileService: Scans a folder for incoming HL7 files.
  • HL7Router: Our new BPL Business Process that builds the dynamic object.
  • MsgRouter: Evaluates the routing rule.
  • HL7FileOperation: Writes the routed message to an output folder.

Step 1: Create the Production

Go to Management Portal > Interoperability > List > Production and click New.

Fill in the form with the following data:

  • Package: Demo
  • Name: HL7RulesDemo
  • Description: Demonstrates dynamic objects and property access pitfalls using a BPL process alongside the HL7 routing engine.
  • Production Type: HL7 Messaging

Click OK.

When you select HL7 Messaging, IRIS automatically creates a set of standard components:

  • HL7FileService: A Business Service that monitors a folder for incoming HL7 files.
  • MsgRouter: An EnsLib.HL7.MsgRouter.RoutingEngine that evaluates routing rules.
  • HL7FileOperation: A Business Operation that writes routed messages to an output folder.
  • BadMessageHandler, EMailAlert, PagerAlert: Alert components you can configure later.

It saves significant setup time, so we will simply reuse these components and add only one new BPL Business Process.


Step 2: Configure the Auto-Created Components

Configure HL7FileService

Click on HL7FileService in the canvas to open its settings panel. Set up the following information:

  • File Path: Your incoming folder (e.g., C:\hl7\in)
  • File Spec: *.hl7
  • Message Schema Category: 2.5

Click Apply.
 

Configure HL7FileOperation

Click on HL7FileOperation in the canvas and set the following:

  • File Path: Your outgoing folder (e.g., C:\hl7\out)

Click Apply.

 

Step 3: Build the BPL Business Process

The MsgRouter routing engine evaluates simple conditions but does not support Code activities for building dynamic objects. We add a BPL Business Process between HL7FileService and MsgRouter to handle all data preparation.

3A. Create the Process

Go to Management Portal > Interoperability > List > Business Processes and click New. In the General tab, set Language to ObjectScript. Then save it as Demo.HL7Router.

3B. Define Context Properties

In BPL, context is a typed class, and every property you want to use must be declared upfront in the Context tab before you write any code. If you skip this and try to set a property inside a Code activity, IRIS will throw a PROPERTY DOES NOT EXIST error at runtime. So, before touching the canvas, let us define all the properties this process will need.

In the right-side panel, click the Context tab, then click + to add each property:

Property Name Property Type
MetaData %DynamicObject
PatientId %String(MAXLEN=50)
PatientSex %String(MAXLEN=50)

3C. Add the Code Activity

Click Add Activity and select Code. Drag it between <start> and <end>. Label it Build Dynamic Object.

Connect the arrows:

  • <start> to Build Dynamic Object
  • Build Dynamic Object to <end>

Click on the Code activity to select it. In the right-side panel, find the Code field and click the magnifier icon next to it. It will open a larger editor where you can enter the code below:

$$$LOGINFO("Request class: " _ $classname(request))
Set dynObj = ##class(%DynamicObject).%New()

// Read values from the actual HL7 message
Set dynObj.MsgType     = request.GetValueAt("MSH:MessageType.MessageCode")
Set dynObj.PatientName = request.GetValueAt("PID:PatientName(1).FamilyName")
Set dynObj.SendingApp  = request.GetValueAt("MSH:SendingApplication")

// Use %Set() for underscore-named properties
Do dynObj.%Set("patient_id",    request.GetValueAt("PID:PatientIDList(1).IDNumber"))
Do dynObj.%Set("patient_sex",   request.GetValueAt("PID:AdministrativeSex"))
Do dynObj.%Set("date_of_birth", request.GetValueAt("PID:DateTimeofBirth"))

// Log each value for testing purposes
$$$LOGINFO("Dynamic Object built:")
$$$LOGINFO("  MsgType    : " _ dynObj.MsgType)
$$$LOGINFO("  PatientName: " _ dynObj.PatientName)
$$$LOGINFO("  SendingApp : " _ dynObj.SendingApp)
$$$LOGINFO("  patient_id : " _ dynObj.%Get("patient_id"))

// Store in context
Set context.MetaData = dynObj
Enter fullscreen mode Exit fullscreen mode

Click OK to close the editor, then click Save and Compile. Ensure that no errors appear before moving forward.

 

3D. Add the Process to Production

Go back to the production canvas. Click Add in the Processes section:

  • Process Class: Demo.HL7Router
  • Process Name: HL7Router
  • Description: BPL process that builds dynamic objects before routing
  • Enable Now: Checked

Click OK.

Before starting the production, make sure that the following steps are complete:

1. Set Business Service Target
Click on HL7FileService in the production canvas. Confirm Target Config Names is set to HL7Router. If not, type HL7Router and click Apply.

2. Enable All Components
Confirm the Enabled checkbox is marked off for the following:

  • HL7FileService
  • HL7Router
  • MsgRouter
  • HL7FileOperation

How It All Fits Together

It is worth pausing here to understand the full flow before we start testing:

  1. HL7FileService picks up the .hl7 file from the incoming folder and parses it into an EnsLib.HL7.Message.  It forwards the message to HL7Router based on the Target Config Names setting.   
  2. HL7Router runs the Build Dynamic Object Code activity, stores the enriched object in context.MetaData, and then calls MsgRouter using SendRequestAsync. MsgRouter evaluates the routing rule and sends the message to HL7FileOperation.   
  3. HL7FileOperation writes the routed message to the outgoing folder.

Step 4: Start the Production and Test

Start the production. All components should show green status indicators.

The Sample HL7 Message

Create the test file using the IRIS terminal to ensure correct segment separators:

Set file = ##class(%File).%New("C:\hl7\in\test1.hl7")
Do file.Open("NWS")
Set content = "MSH|^~\&|SENDING_APP|SENDING_FAC|RECEIVING_APP|RECEIVING_FAC|20250315143022||ADT^A01|MSG00001|P|2.5" _ $Char(13)
Set content = content _ "EVN|A01|20250315143022" _ $Char(13)
Set content = content _ "PID|1||MRN001^^^HOSP||Smith^John^M||19850101|M|||123 Main Street^^Lahore^Punjab^54000^PK||03001234567" _ $Char(13)
Set content = content _ "PV1|1|I|WARD-A^Room101^Bed1|E|||DOC001^Smith^James|||SUR|||||||V01|ACC001" _ $Char(13)
Do file.Write(content)
Do file.Close()
Write "File created", !
Enter fullscreen mode Exit fullscreen mode

(Remember to replace C:\hl7\in\ with your actual incoming folder path.)

Verify in Message Viewer and Visual Trace

Within 5 seconds, the adapter picks up the file. Go to Management Portal > Interoperability > View > Messages.

Click the message to open the Visual Trace. You will see the complete flow:

  • HL7FileService reads the file.
  • HL7Router processes it and builds the dynamic object.
  • MsgRouter evaluates the routing rule.
  • HL7FileOperation writes the output file.


Common Mistakes

Not defining context properties upfront: If you try to set context.MetaData without first declaring it in the Context tab, IRIS will throw PROPERTY DOES NOT EXIST at runtime. Always define all context properties before writing any code.

Setting the Target Config Names after adding the process: If you add HL7Router to production but forget to update HL7FileService Target Config Names to point to it, messages will bypass HL7Router entirely and go directly to MsgRouter. Always confirm the target after adding a new component.


Practical Recommendations

  • Always define all context properties in the Context tab before writing any Code activity logic.
  • Log every dynamic object property immediately after setting it during development; this is the fastest way to confirm values are correct before testing the routing rule.
  • Keep the dynamic object lean by only storing values you actually need downstream.

FAQ

Q: Do I need a dynamic object if I can pass the HL7 message directly to the routing rule?

A: Simple property names (e.g., context.MetaData.MsgType = "ADT") work well and get evaluated correctly. However, properties with underscores in their names do not work reliably in the rule expression editor. Extract those values into typed context variables in the Code activity and evaluate them in the rule instead.

Q: Why does my Code activity compile but fail at runtime with PROPERTY DOES NOT EXIST?

A: This happens when you reference a context property that was not declared in the Context tab. Go to the Context tab, add the missing property, and recompile.

Q: Can I use %DynamicObject properties directly in a routing rule condition?

A: Simple property names work context.MetaData.MsgType = "ADT" evaluates correctly. Properties with underscores in their names do not work reliably in the rule expression editor. Extract those values into typed context variables in the Code activity and evaluate those in the rule instead.

Q: What happens if I forget to call Compile after editing the BPL?

A: The production will run the previous compiled version of the class. Your changes will not take effect until you compile. Always compile after every code change and confirm there are no errors before testing.

Q: Can I store a nested object inside a %DynamicObject?

A: Yes. You can set a nested %DynamicObject as a property value using %Set(). However, to access nested properties, you should get the nested object first. (It will be covered in detail in Part 2.)


What Is Coming in Part 2

Part 1 covered building and using a %DynamicObject inside a BPL Business Process. Part 2 will use the same production but add a second Code activity that will demonstrate seven real-world property-access pitfalls.


Conclusion

Dynamic objects are a practical tool that enriches HL7 messages with metadata before routing decisions are made. The key is to build them in a BPL Code activity, logging every property during development, and storing them in context variables that downstream components can evaluate cleanly.

The production we built in this article gives you a working foundation to experiment with. Try adding more properties to the dynamic object, or update the routing rule condition to evaluate context.MetaData.MsgType directly and see how it behaves. In the next article in this series, we will take this exact production and break down seven real-world property-access pitfalls every developer encounters when working with %DynamicObject in production integrations.

Thanks for reading!

Top comments (0)