# Mirth Connect channel architecture: source, filter, transformer, destination

> How to structure NextGen Connect (formerly Mirth Connect) channels — source, filter, transformer, destination, configuration maps, error queues versus retry, and Git export of channel XML.

Author: Ala Ben Aicha

Canonical page: https://alabenaicha.me/insights/mirth-connect-channel-architecture

Updated: 2026-09-17

## Direct answer

A NextGen Connect (Mirth Connect) channel is source, filter, transformer, destination. Put environment URLs in the configuration map, not the channel XML. Error queues are not retries. Export channels to Git.

## Channel mapping

**Mirth Connect** is the product name most interface engineers still search. The vendor name is **NextGen Connect** (Mirth Connect by NextGen Healthcare). On 19 March 2025, with version **4.6**, NextGen moved new releases to a commercial licence; source for 4.6+ is not on GitHub. Older source, upgrade notes and the [FAQ](https://github.com/nextgenhealthcare/connect/wiki/Frequently-Asked-Questions) remain. This page is channel architecture, not a licence opinion.

A channel is one pipeline:

```
Source connector
    → source filter (accept / drop)
    → source transformer (normalize)
    → destination 1..n
         → destination filter
         → destination transformer
         → send + response
    → postprocessor
```

| Stage                       | What it does                                                                     | What it must not do                                       |
| --------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------- |
| **Source**                  | Receive or poll: TCP/MLLP listener, HTTP, file, database, JMS, channel reader    | Encode the whole mapping; that belongs in transformers    |
| **Source filter**           | Boolean: process this message at all (MSH-9, sending facility, basic structure)  | Mutate `msg`; filters that change data hide failures      |
| **Source transformer**      | Normalize: namespaces, dates, identifier systems, FHIR JSON                      | Call production HTTP without a destination queue in front |
| **Destination filter**      | Route subsets (A01 to registration, A08 to demographics, ORU to the lab channel) | Duplicate the source filter without a reason              |
| **Destination transformer** | Build the outbound payload for *that* system                                     | Hard-code hostnames, ports, or secrets                    |
| **Destination**             | Deliver (MLLP, HTTP FHIR, file, channel writer)                                  | Be the only copy of environment configuration             |

Maps (from the [NextGen variable-map list](https://docs.nextgen.com/mirthc2ae-connect-by-nextgen-healthcare-user-guide-3281761/variable-maps-15397)):

| Map               | JS / shortcut               | Lifetime                                                                                  |
| ----------------- | --------------------------- | ----------------------------------------------------------------------------------------- |
| Connector         | `connectorMap` / `$co`      | This connector, this message                                                              |
| Channel           | `channelMap` / `$c`         | This channel, this message                                                                |
| Source            | `sourceMap` / `$s`          | From the source connector                                                                 |
| Global channel    | `globalChannelMap` / `$gc`  | This channel, process lifetime                                                            |
| Global            | `globalMap` / `$g`          | Server process                                                                            |
| **Configuration** | `configurationMap` / `$cfg` | **File** on disk (`configuration.properties` under appdata), not the in-memory global map |

The configuration map is the environment file: FHIR bases, MLLP hosts, path prefixes. NextGen stores it as a flat file and **does not include it in server configuration exports**, so the same channel XML can move across test and production. That is the point.

Hybrid v2 plus FHIR still sits on this pipeline — the [v2-to-FHIR migration playbook](https://alabenaicha.me/insights/hl7v2-to-fhir-migration-strategy). Order/result identifiers belong in [radiology and lab workflows](https://alabenaicha.me/insights/radiology-lab-workflow-integration), not in a second copy of those maps inside every transformer.

## Identifier traps

| Trap                                              | Symptom                                                  | Fix                                                                                   |
| ------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Hostnames in transformer JavaScript               | Channel XML differs per environment; Git diffs are noise | `$cfg('fhir.base')` / `$cfg('his.mllp.host')` in connector properties                 |
| Config map keys colliding with `globalChannelMap` | Wrong URL because lookup precedence hits `$gc` first     | Do not reuse config-map names in other maps                                           |
| Source filter too tight                           | A08 never updates FHIR Patient                           | Filter on message *family* (ADT) at source; split A01/A08 at destination              |
| One channel for ADT + ORM + ORU                   | Shared transformer with a forest of `if (msg['MSH']…)`   | One channel per inbound contract, or a thin source that channel-writes to specialists |
| Treating ERROR status as “will retry”             | Silent loss after max retries                            | See queues below                                                                      |
| Exporting channels without code templates         | Import misses shared functions                           | Export libraries with the channel, or version templates in the same Git repo          |

HL7 field indexing in Rhino is 1-based (`msg['PID']['PID.3']['PID.3.1']`). Empty fields throw if you call `.toString()` without a guard — that is a transformer bug, not a “bad ADT”.

## Test fixtures

De-identified. Channel XML is huge; keep fixtures as **messages + expected outbound**, not as screenshots of the administrator.

Inbound ADT (same Jane Example as the ADT article):

```
MSH|^~\&|HIS|HOSP-A|MIRTH|HOSP-A|20260917103000||ADT^A01^ADT_A01|MSG0001|P|2.5
PID|1||HOSP-MRN-0001^^^HOSP-A^MR||EXAMPLE^JANE^Q||19770412|F
PV1|1|I|||||||||||||||||VN-2026-00042^^^HOSP-A^VN|||||||||||||||||||||||||20260917103000
```

Configuration map (never commit real secrets; this is a shape):

```
his.mllp.host=his-test.example.org
his.mllp.port=2575
fhir.base=https://fhir-test.example.org/fhir
fhir.patient.mrn.system=https://hosp-a.example.org/mrn
```

Destination HTTP body (truncated Patient):

```json
{
  "resourceType": "Patient",
  "identifier": [
    {
      "system": "https://hosp-a.example.org/mrn",
      "value": "HOSP-MRN-0001"
    }
  ],
  "name": [{ "family": "EXAMPLE", "given": ["JANE", "Q"] }],
  "birthDate": "1977-04-12"
}
```

Minimum tests:

1. Source filter: ADT^A01 accepted; ORM dropped (or routed elsewhere).
2. Transformer guard: PID-3 empty → filtered or ERROR with a reason, not a `TypeError`.
3. Config map: same channel XML against test and production bases.
4. Destination queue: stop the FHIR server, send A01, message QUEUED, server returns, message SENT once.
5. Git: export channel XML, change one transformer line, diff shows that line — not a 4,000-line property reorder.

Never paste live PHI into channel descriptions, test messages stored in Git, or the administrator’s “Generate” pane on a production box.

## Failure modes and rollback

Destination queues are not the error table.

| Mode           | Behaviour                                       | Use                                                         |
| -------------- | ----------------------------------------------- | ----------------------------------------------------------- |
| **Never**      | Failed send fails the destination immediately   | Only if a later destination must see a synchronous response |
| **On failure** | Try once, then queue                            | Default for FHIR HTTP and MLLP toward brittle receivers     |
| **Always**     | Queue first, send async; downstream sees QUEUED | High volume; accept out-of-order if you also enable rotate  |

ERROR means the message is **not** going to retry by itself. Operators reprocess, or you route failures to an error channel (channel writer) that pages a human. Retry is the destination queue. Mixing the two is how ADT A03 sits behind a poison A08 until census is fiction.

Git export: channels live as XML in the engine database. There is no native “the repo is the runtime”. Practical pattern: REST export each channel to one file (`{id}-{name}.xml`), pretty-print for diffs, pull request, import + deploy on the target. Do not store `configuration.properties` in the same repo as production secrets; store **keys** and inject values per environment.

| Failure                 | Detection                                                          | Rollback                                                                                   |
| ----------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| Transformer exception   | Connector ERROR, MLLP still ACKed if you ACK’d at source too early | ACK after destination success, or ACK at source and accept that you now own replay         |
| Queue backup            | QUEUED count climbing                                              | Pause source; fix receiver; do not halt-and-wipe the queue                                 |
| Wrong environment       | Test Patient on production FHIR                                    | Config map + separate client IDs; disable the production destination in lower environments |
| Loop via channel writer | CPU cliff                                                          | Source map of MSH-3; drop messages this engine just emitted                                |

Disable a bad writer by undeploying **that destination** (or the channel) without touching the HIS feed. Keep the inbound MLLP up if clinical senders cannot buffer.

## Related service

If you need NextGen Connect / Mirth channels designed, exported, and tested (ADT, ORM/ORU, FHIR HTTP), that is [Mirth Connect](https://alabenaicha.me/services/mirth-connect). For a scoped channel-architecture spike, use [contact with project intent](https://alabenaicha.me/contact?intent=project).

This page is not a NextGen licence, not Channel History as a substitute for Git, and not a throughput benchmark.
