Validation Pipeline
Four validation stages, four error families, four distinct responses — the pattern that makes partner errors actionable.
Problem
An EDI hub receives an INVOIC that gets rejected. The reject says "invalid message". But: is the EDIFACT structure broken? An unknown qualifier? A business rule ("the invoice references a non-existing PO")? A specific Walmart constraint ("supplier number must be 9 digits")? Without distinction, the EDI support operator does not know who to call (partner supplier? internal business team? partner IT?). Handling is undifferentiated: everything goes to DLC, we spend 4h diagnosing.
Forces
- Errors come from different sources. Partner software bug, business misunderstanding, partner IG misread, inconsistent data.
- Handling differs. Syntactic error → technical retry. Business error → business escalation. IG error → partner coordination.
- Different ACKs exist. CONTRL (EDIFACT syntactic), APERAK (EDIFACT application), 999 (X12 implementation), 824 (X12 application advice), NRR (AS4).
- Validation order matters. No point checking business if syntax is broken.
Solution
Build an ordered four-level pipeline. 1. Syntactic: schema conformance (EDIFACT message definition, UBL XSD, X12 transaction set). 2. Semantic: code and qualifier validity (Schematron on UBL, EN 16931 BR-CO rules, UN/EDIFACT tables). 3. Business: business rules (reference to existing PO, invoice duplicate, amount consistent with lines). 4. Partner: partner Implementation Guide (Walmart custom rules, PEPPOL country profile). Each failure produces an appropriate ACK and a typed event. The pipeline stops at the first failure — no need to test IG on broken syntax.
Inbound message (EDIFACT INVOIC, X12 810, UBL Invoice)
│
▼
┌────────────────────────────────┐
│ 1. Syntactic validation │
│ D.96A schema / UBL XSD │
│ structure, types, required │
└──────┬─────────────────────────┘
fail │ → negative CONTRL / 999 / NRR fault → technical DLC
ok ▼
┌────────────────────────────────┐
│ 2. Semantic validation │
│ qualifiers, codes, enum │
│ Schematron, EN 16931 rules │
└──────┬─────────────────────────┘
fail │ → APERAK / 824 → semantic DLC
ok ▼
┌────────────────────────────────┐
│ 3. Business validation │
│ business rules (coherence, │
│ duplicates, authorisation) │
└──────┬─────────────────────────┘
fail │ → APERAK + escalation → exception flow
ok ▼
┌────────────────────────────────┐
│ 4. Partner validation │
│ Walmart IG, Stellantis │
│ specific, proprietary codes │
└──────┬─────────────────────────┘
fail │ → partner functional → partner workflow
ok ▼
Accepted message → event broker
EDI implementation
Concrete case: Factur-X hub receives an electronic invoice. Step 1
— UBL 2.1 XSD validates XML structure, returns a FHIR-like OperationOutcome structured if fail. Step 2 — EN
16931 Schematron (BR-CO-01 to BR-CO-25, BR-DE-XX for Germany,
BR-FR-XX for France via Factur-X): tax/total consistency, SIRET
format, EC valid currency. Step 3 — internal business rules: "the
referenced PO exists", "not already invoiced", "amount consistent
with received ASN". Step 4 — specific IG: for Walmart, GS06 must
be the EIN; for PEPPOL FR, IBAN mandatory. Each step emits: InvoiceSyntacticallyValid, InvoiceSemanticallyValid, InvoiceBusinessValid, InvoicePartnerValid, or typed failure variants. The
ops operator has a per-stage dashboard: they know to the second
whether there is an EN 16931 issue (internal team) or a custom
Walmart problem (escalate to partner). Step 2 tools: Saxon-EE
Schematron, the OpenAPI KoSIT
validator (EN 16931 reference), peppol-validator.
Anti-patterns
- Monolithic validation. "The message is valid or not": no diagnosis, lost operator.
- Business validation in step 1. Testing "PO exists" on a message with broken syntax: false positives, false negatives.
- HTTP 500 instead of negative ACK. The partner does not know validation failed — believes the hub is down.
- Validation without versioning. An EN 16931 rule changes in 2027, we lose valid 2025 messages. Version pipelines.
- Validation after enrichment. Enriching before validating consumes resources on messages destined to be rejected. Validate first (at least steps 1+2).
Related patterns
- Pipes and Filters — general topology.
- Invalid Message Channel — destination of semantically invalid messages.
- Dead Letter Channel — destination of syntactically invalid messages.
- Acknowledgements — each stage emits its ACK type.
- Enrichment Pipeline — sibling pattern, to run after the validation pipeline.
- Exception flow — each failure type triggers its flow.
Sources
- OpenPEPPOL — PEPPOL Validation Profile. The reference Schematron to validate a PEPPOL invoice. docs.peppol.eu — UBL Invoice
- EN 16931 — Validation rules. The BR-XX rules defining the European standard semantics. cen.eu
- KoSIT Validator. The open-source reference EN 16931 validator. github.com/itplr-kosit/validator
- UN/EDIFACT CONTRL D.96A. EDIFACT syntactic ACK. unece.org — CONTRL D.96A
- UN/EDIFACT APERAK D.96A. Application/semantic ACK. unece.org — APERAK D.96A
- X12 TR3 999 Implementation Acknowledgment. Evolution of 997 for HIPAA validation. x12.org