Content Enricher
An EDI message almost never carries everything downstream needs. The Content Enricher fills the gaps by consulting an external reference: GLN → address, GTIN → label, country code → long name. The pattern of context injection.
Problem
EDIFACT and X12 messages were designed to be compact: they carry identifiers (13-digit GLN, 13-digit GTIN, 14-digit SIRET) rather than labels. The sender assumes the receiver can resolve these identifiers to associated information — name, address, VAT, product category. But when feeding an ERP, an invoicing system or a supplier portal, we often need those fields in clear text. Without enrichment, those lookups happen at 200 places in downstream code.
Forces
- Protocol compactness vs application richness. EDIFACT optimises 1980s bandwidth. Modern applications want everything in clear.
- External source of truth. The reference (Master Data Management, ERP) is the authority — not the transient message.
- Lookup cost. Each lookup has a cost (latency, network). For an ORDERS with 200 lines, 200 sequential lookups become prohibitive.
- Temporal consistency. The name associated with a GLN can change (rebranding, merger). We must choose between freezing enrichment at receipt time or re-resolving at read time.
Solution
EIP §336 (Hohpe & Woolf, 2003) models the Content Enricher as a component that: (a) receives a canonical message, (b) extracts identifiers to enrich, (c) consults an external reference, (d) injects resolved fields into the message and (e) republishes. Lookup can be synchronous (immediate) or asynchronous (pre-cache updated by a separate flow).
Canonical ORDERS Content Enricher Enriched ORDERS
(GLN only) (resolves GLN → addr)
───────────── ───────────────── ───────────────
{ {
"buyer": { ┌─────────────┐ "buyer": {
"gln": "871234..." ──▶ external │ "gln": "871234...",
} │ partner-db │ "name": "Carrefour",
} │ ├──▶ "address": {
└─────────────┘ "street": "...",
"city": "Massy",
"country": "FR"
}
}
} Topology
Three main variants:
- Synchronous Enricher. Lookup on every message. Simple, but sensitive to reference latency and availability.
- Cached Enricher. Lookup with TTL cache (10 minutes to 24 hours per domain). Drastically reduces load on the reference.
- Pre-loaded Enricher. Local replica of the reference kept up to date by changefeed (CDC, master data events). Near-zero latency, but requires an event-driven master-data infrastructure.
EDI implementation
Three emblematic enrichments:
- GLN → partner record. The partner sends
NAD+BY+8712345600014::9. The enricher resolves to a partner record with name, address, IBAN, VAT, contact. The lookup table is typically the ERP's customer/supplier database. - GTIN → product. The partner sends
LIN+1++4006381333931:EN. The enricher resolves to a product record with label, category, packaging, VAT rate. The lookup table is the product catalogue (PIM). - ISO 3166 country code → name + tax zone. The
message carries
FR; the enricher injects "France", EU zone, default currency EUR. Case of a static embedded reference (no external lookup needed).
Before enrichment:
{
"type": "ORDER",
"number": "PO-12345",
"buyer": { "gln": "8712345600014" },
"supplier": { "gln": "5412345600015" },
"lines": [
{ "item": { "gtin": "4006381333931" }, "quantity": 24 }
]
} After enrichment:
{
"type": "ORDER",
"number": "PO-12345",
"buyer": {
"gln": "8712345600014",
"name": "Carrefour Centrale Massy",
"address": {
"street": "33 Avenue Émile Zola",
"city": "Massy",
"postalCode": "91300",
"country": "FR"
},
"vatId": "FR12652014051"
},
"supplier": {
"gln": "5412345600015",
"name": "Brasserie XY",
"vatId": "BE0405841683"
},
"lines": [
{
"item": {
"gtin": "4006381333931",
"name": "Pelikan rollerball ink black",
"category": "stationery"
},
"quantity": 24
}
]
} Cache, freshness and failure policy
Three questions to settle:
- What TTL? For relatively stable partner data (name, address), 24 hours is reasonable. For more volatile data (product price, stock), 5-15 minutes or no cache.
- What if lookup fails? Three options: (a) block
the message in a Dead Letter Channel
for review, (b) pass the message downstream without enrichment
with an
enrichmentSkipped: trueflag, (c) apply a documented default value. The right answer depends on business criticality. - What to freeze in the message? If the order must survive a reference change (a GLN that changes name), freeze the snapshot of enriched fields. Otherwise, only store the identifier and re-resolve at read time.
Anti-patterns
- Synchronous lookup without timeout. If the reference takes 30 seconds to answer, we block the EDI pipeline for 30 seconds. Always set a strict timeout (1-3 seconds) with circuit breaker.
- Lookup inside a loop. 200 lines = 200 sequential calls = unacceptable. Bulk lookup is mandatory.
- No cache. For stable data, not caching pointlessly multiplies load on the ERP.
- Cache without invalidation. A 24h cache with no invalidation mechanism will propagate stale data for 24h after a master change. If the reference publishes mutation events, the enricher must subscribe.
- Silent partial enrichment. If only half of the fields could be resolved, the message must explicitly signal it. The downstream consumer must see the trace of incompleteness.
Related patterns
- Canonical Model — the canonical must foresee optional fields for enriched data.
- Circuit Breaker — protect the pipeline from a reference outage.
- Content-Based Router — often placed after enrichment, because the decision needs resolved fields.
Sources
- Hohpe G., Woolf B. — Enterprise Integration Patterns, Content Enricher (§336). enterpriseintegrationpatterns.com — Content Enricher
- GS1 General Specifications, section 3 — Global Location Number (GLN) and Global Trade Item Number (GTIN). The reference for identifiers to enrich. gs1.org — General Specifications
- ISO 3166-1. Country codes and their mapping to long names.
- AWS Architecture Blog — Caching strategies for ETL pipelines. Modern TTL cache and changefeed invalidation patterns. aws.amazon.com/blogs/architecture