ediverse Explore the platform

Spotlight PEPPOL BIS Billing 3.0 The EU e-invoicing mandate is here — France Sept 2026, Belgium Jan 2026, Germany 2025.

Internal canonical model

Without a pivot, every new partner adds as many mappings as you have target systems. With a pivot, you only ever map to the pivot. The cost is linear instead of quadratic.

Problem — the n × m mapping

An EDI platform must, by construction, talk to several partners (each with their own standard: EDIFACT, X12, UBL, cXML) and feed several target systems (ERP, WMS, TMS). If every mapping is written directly from partner to system, the result is a Cartesian product: with 10 partners and 4 systems, that is 40 mappings to maintain. Adding a partner adds 4 mappings; adding a system adds 10. That n × m growth is unsustainable beyond a few years.

plaintext mapping-comparison.txt
Without canonical — n × m mapping:

  Partner A (EDIFACT)  ┐                ┌─── ERP X
  Partner B (X12)      ├── 12 mappings ─┤
  Partner C (cXML)     │   (3 × 4)      ├─── ERP Y
  Partner D (UBL)      ┘                └─── ERP Z

With canonical — n + m mapping:

  Partner A ──┐                  ┌── ERP X
  Partner B ──┤                  ├── ERP Y
  Partner C ──┼─► CANONICAL ─────┤
  Partner D ──┘   (1 schema)     └── ERP Z

  → 7 mappings instead of 12 (4 IN + 3 OUT)

Forces

  • Real partner heterogeneity. You can't force every partner onto a single standard — they stay on their EDIFACT D.96A legacy, X12 .004010 or cXML 1.2.069.
  • Real system heterogeneity. ERP, WMS, BI and customer portal each have their own internal schema, often dictated by the vendor (SAP IDoc, NetSuite REST, Salesforce SOQL).
  • Semi-annual evolutions. EDIFACT and X12 ship two releases per year. Without a pivot, every update propagates across all outbound mappings.
  • Stability of the pivot. The canonical model has to evolve slower than the periphery, otherwise it loses its value. That is a strong design constraint.

Solution: a canonical in the middle

The Canonical Data Model pattern (Hohpe & Woolf, 2003) prescribes a single internal schema — the canonical — used as a pivot between peripherals. Each partner has an IN mapping translating its format to the canonical, and each target system has an OUT mapping translating the canonical to its internal format. The canonical itself is never exposed.

The canonical is typically defined in JSON Schema (Draft 2020-12) or XSD. JSON makes modern tooling easier (generated TypeScript types, Zod/Ajv validation), but XSD remains a better fit when aligning to a public canonical (OAGi BODs, UBL).

Example of a canonical ORDER JSON

Here is what a minimal pivot order can look like:

json canonical-order-v3.json
{
  "$schema": "https://ediverse.example.com/schemas/order/v3",
  "kind": "Order",
  "version": "v3",
  "header": {
    "id": "ORD-2026-00187",
    "issuedAt": "2026-05-14T10:18:00Z",
    "currency": "EUR",
    "buyer": {
      "gln": "5410000000123",
      "name": "North Textile Cooperative",
      "address": { "country": "FR", "city": "Lyon" }
    },
    "seller": {
      "gln": "5410000000456",
      "name": "Atelier Marchand SARL"
    }
  },
  "lines": [
    {
      "id": "1",
      "itemId": { "scheme": "gtin", "value": "3520000001234" },
      "description": "Unbleached canvas 220 g/m²",
      "quantity": { "value": 120, "unit": "MTR" },
      "unitPrice": { "value": 18.00, "currency": "EUR" }
    }
  ]
}

Key principles:

  • Typed identifiers. itemId has an explicit scheme (gtin, sap-mat, …). This prevents mixing GTIN and internal SKU eighteen months down the line.
  • Versioned canonical. The v3 in the schema marks the third revision. Older v2 mappings can coexist during a migration window.
  • Explicit units. quantity.unit in UN/ECE Recommendation 20 codes (MTR = metre, PCE = piece). Never assume a default unit.
  • No untyped optional field. If a partner has an uncovered business need, add an explicit field to the canonical — do not allow a free-form extra sub-object.

Off-the-shelf — OAGi BODs and UBL

Rather than invent a canonical, you can adopt a public one:

  • OAGi BODs (Business Object Documents) — Open Applications Group, since 1995. Catalogue of more than 700 BODs covering orders, invoices, payments, HR, manufacturing. Adopted by Oracle Fusion and SAP S/4HANA as an integration schema. oagi.org
  • UBL (Universal Business Language) — OASIS, version 2.4 (2023). More oriented toward billing/procurement, technical foundation of PEPPOL BIS Billing 3.0. docs.oasis-open.org/ubl

Both come with a stable maintainer and a tooled community (parsers, validators). Drawback: they are large and usually cover more than what you want internally. Common practice is to profile a public canonical, i.e. extract a documented subset that becomes the internal canonical.

Governance of the canonical

The pattern only works if the canonical is governed like a public API:

  • Semantic versioning. v1, v2, … with clear rules for breaking changes (new major) vs additive ones (minor).
  • Migration window. When v3 ships, v2 stays supported for 6 to 12 months. Existing IN/OUT mappings don't have to migrate the same weekend.
  • Validation on entry and exit. An IN mapping that produces an invalid canonical must be rejected before any business processing. An OUT mapping consuming a canonical must validate it on entry.
  • Derived documentation. The canonical is the single source of truth: schemas, TypeScript types, partner docs and integration tests are all generated from it. No manual copies.

Anti-patterns

  • Catch-all canonical. A schema with customFields: Record<string, any> where each mapping dumps whatever it wants. No more typing, no more guarantee: that is not a canonical, that is a backpack.
  • Canonical = vendor format. Adopting SAP IDoc as the canonical because everything flows through SAP is tempting and catastrophic the day SAP is replaced. The canonical must be vendor neutral.
  • Implicit versioning. Adding a mandatory field without bumping the version silently breaks every existing mapping.
  • Direct partner → system mapping bypassing the canonical "to go faster". You rebuild the n × m you wanted to avoid.
  • Over-rich canonical. Importing UBL whole into your internal canonical without profiling gives you 600+ elements 95% of which serve no purpose — each mapping becomes an exegesis.
  • Acknowledgements — the business ACK returned to the partner is generated from the canonical.
  • Exception flow — canonical invalidation is one of its entry points.

Sources