Skip to content

Open-M: The Integration Controller

UTL-X transforms data. But in a real integration landscape, transformation is one step in a larger flow: receive a message, route it to the right transformation, validate the output, deliver it to the target, handle errors, retry on failure, log for compliance. That orchestration layer is what Open-M provides.

Open-M is in active development. This chapter previews the architecture and concepts. UTL-X is fully functional without Open-M — you can use it standalone with Docker, Dapr, or any orchestration tool. Open-M is an optional layer on top.

What Is Open-M?

Open-M is an open-source integration controller written in Go. It manages the lifecycle of messages as they flow between systems — receiving, transforming, validating, routing, and delivering.

Think of it as:

  • Open-M = the orchestra conductor (decides what happens, when, and where)

  • UTL-X = the musicians (perform the transformation work)

 

┌─────────────────────────────────────────────────────┐
│               Open-M Controller (Go)                │
│                                                     │
│  Receive → Route → ┌──────────┐ → Validate →        │
│  (broker)  (rules) │  UTLXe   │   (schema)          │
│                    │  (via Go │                     │
│                    │  wrapper)│   Deliver → (target)│
│                    └──────────┘                     │
│                                                     │
│  Error handling: retry, dead-letter, alert          │
│  Monitoring: Prometheus, audit trail                │
└─────────────────────────────────────────────────────┘

Why Open-M + UTL-X?

Separation of concerns:

Open-M handlesUTL-X handles
WHERE data goes (routing)WHAT happens to data (transformation)
WHEN to process (scheduling, triggers)HOW to transform (expressions, functions)
Error handling (retry, dead-letter)Data validation (schema, assertions)
Monitoring and audit trailFormat conversion and enrichment
Connector management650+ stdlib functions

Each does one thing well. UTL-X can be used WITHOUT Open-M (standalone CLI, engine, Docker). Open-M can use other transformation engines — but UTL-X is the default.

MPPM: Message Processing Pipeline Model

What Is MPPM?

MPPM (Message Processing Pipeline Model) is Open-M's core concept: every integration is a message pipeline — an ordered sequence of processing stages that a message flows through.

Like BPMN standardizes business processes, MPPM standardizes message flows. It defines:

  • How messages flow through processing stages

  • What happens at each stage (transform, validate, route, enrich)

  • How errors are handled (retry, dead-letter, compensate)

  • How stages compose into flows

Pipeline Stages

┌─────────┐  ┌──────────┐  ┌───────────┐  ┌──────────┐  ┌─────────┐
│ Receive │→ │ Transform │→│  Validate │→ │  Route   │→ │ Deliver │
│ (input) │  │ (UTL-X)  │  │ (schema)  │  │ (logic)  │  │(output) │
└─────────┘  └──────────┘  └───────────┘  └──────────┘  └─────────┘
      │            │             │              │              │
      └────────────┴─────────────┴──────────────┴──────────────┘
                          Error handler (dead-letter, retry, alert)

Each stage is a plugin — swap implementations without changing the pipeline definition. UTL-X is the default Transform stage plugin. The Validate stage uses UTLXe's schema validators. The Receive and Deliver stages connect to message brokers, APIs, file systems, or databases.

The Message Envelope

Every message in Open-M travels in a standard envelope:

json
{
  "messageId": "msg-550e8400",
  "correlationId": "flow-xyz-001",
  "timestamp": "2026-04-30T10:30:00Z",
  "source": "dynamics365",
  "contentType": "application/json",
  "headers": {
    "transformId": "order-to-invoice",
    "priority": "normal",
    "retryCount": 0
  },
  "payload": "{\"orderId\": \"ORD-001\", ...}"
}

The envelope carries metadata about the message — ID, correlation, source, content type, processing headers. The payload carries the actual data. UTL-X transforms the payload. Open-M manages the envelope.

This separation means UTL-X never needs to know about routing, retries, or correlation — it receives a payload, transforms it, returns the result. Open-M handles everything else.

How MPPM Maps to UTL-X

MPPM conceptUTL-X equivalent
PipelinePipeline chaining (Chapter 20)
Transform stage.utlx transformation file
Validate stageSchema validation (Chapter 19)
Message payload$input
Message headersAccessible via transport headers
Error handlingtry/catch in .utlx, dead-letter in pipeline
Stage configurationtransform.yaml / output options
Multiple pipelinesMultiple UTLXe bundles

A single UTL-X transformation IS an MPPM Transform stage. An UTLXe pipeline IS an MPPM pipeline restricted to transformation. Open-M adds routing, error handling, monitoring, and non-transformation stages around it.

Open-M Architecture

Go-Based Controller

Open-M is written in Go — lightweight, fast startup, single binary, native Kubernetes integration. It communicates with UTLXe via the Go wrapper (Chapter 34) using protobuf over stdio.

Why Go for the controller and Kotlin/JVM for the engine?

  • Controller (Go): needs fast startup, low memory, native Kubernetes client, lightweight HTTP server. Go excels here.

  • Engine (Kotlin/JVM): needs 650+ stdlib functions, schema validation, bytecode compilation, SnakeYAML/Jackson parsers. The JVM ecosystem provides these.

The Go wrapper bridges them — the controller spawns UTLXe as a subprocess and communicates via the uniform SDK interface (Chapter 34).

Flow Definitions

Integration flows are defined in YAML:

yaml
name: order-to-invoice
version: "1.0"
stages:
  - name: receive
    type: azure-service-bus
    config:
      queue: orders-inbound

  - name: transform
    type: utlx
    config:
      transformation: order-to-ubl.utlx
      strategy: COMPILED

  - name: validate
    type: schema
    config:
      schema: ubl-invoice-2.1.xsd
      policy: STRICT

  - name: deliver
    type: http
    config:
      url: https://peppol-ap.example.com/submit
      method: POST

  errorHandler:
    type: dead-letter
    config:
      queue: orders-dlq
      maxRetries: 3
      retryDelay: PT30S

This YAML defines a complete integration flow: receive from Service Bus, transform with UTL-X, validate against UBL XSD, deliver to Peppol Access Point, dead-letter on failure with 3 retries. The .utlx file is referenced by name — change the transformation without touching the flow definition.

Plugin Architecture

Every stage type is a plugin:

  • Receive plugins: Azure Service Bus, Kafka, GCP Pub/Sub, AWS SQS, HTTP webhook, file watcher, MQTT

  • Transform plugins: UTL-X (default), pass-through, custom (via HTTP call)

  • Validate plugins: UTLXe schema validator, custom (via HTTP call)

  • Route plugins: content-based routing, header-based routing, round-robin

  • Deliver plugins: HTTP, Service Bus, Kafka, file, SMTP, database insert

Adding a new connector means implementing the plugin interface — not modifying the core controller.

What This Means for UTL-X Users

If You're Using UTL-X Standalone

Nothing changes. UTL-X works perfectly on its own — CLI for development, UTLXe for production, Docker for deployment. Open-M is optional.

If You Need Orchestration

When you outgrow "just transformation" and need routing, retry logic, dead-letter handling, flow monitoring, and audit trails — Open-M provides it. Your .utlx files work unchanged in Open-M. Your UTLXe deployment works unchanged. You don't rewrite anything — you wrap your transformations in MPPM flow definitions.

The Growth Path

Level 1: utlx CLI              (development, scripting)
Level 2: UTLXe container        (production, high throughput)
Level 3: UTLXe + Dapr           (broker integration, cloud-native)
Level 4: Open-M + UTLXe         (full orchestration, enterprise-grade)

Each level adds capabilities without replacing the previous one. Your .utlx files, your conformance tests, your schemas — all carry forward unchanged.

Released under AGPL-3.0.