Pipes and Filters
Le pattern de structure d'architecture le plus cité du catalogue. Un pipeline d'intégration EDI n'est pas un gros bloc : c'est une chaîne d'étages atomiques (parse, valider, enrichir, traduire, livrer) reliés par des canaux. Chaque étage est isolable, testable, remplaçable.
Problème
Un message EDI entrant doit subir une dizaine de transformations avant d'atterrir dans l'ERP : détecter le format, parser, valider la syntaxe, valider les règles métier, résoudre les identifiants, traduire vers le canonique, traduire vers le schéma de l'ERP, livrer. Écrire ce parcours comme une seule fonction monolithique produit du code intransportable : tout changement impacte tout, le test unitaire devient impossible, on ne peut pas changer une étape sans risquer les autres. Il faut décomposer le pipeline.
Forces
- Séparation des responsabilités. Chaque étage résout une seule classe de problème. La parseuse ne valide pas, le validateur ne traduit pas.
- Composabilité. Ajouter une étape (par exemple anonymisation pour le RGPD) = brancher un nouveau filtre entre deux existants, pas réécrire.
- Testabilité unitaire. Chaque filtre a un contrat de message en entrée et en sortie ; on le teste isolément avec des golden files.
- Parallélisme natif. Plusieurs messages peuvent traverser le pipeline en parallèle, chaque filtre étant un consommateur indépendant sur son canal d'entrée.
- Coût d'orchestration. Chaque filtre = un processus, une queue, un déploiement. Plus de pièces à monitorer.
Solution
EIP §70 (Hohpe & Woolf, 2003), repris depuis l'architecture UNIX (Doug McIlroy, 1964, formalisé dans Software Tools de Kernighan & Plauger, 1976). Décomposer le traitement en filtres — unités atomiques qui consomment un message sur un canal d'entrée, le transforment, le déposent sur un canal de sortie — reliés par des pipes (les canaux). Chaque filtre respecte un contrat : même type de message en entrée et en sortie (l'enveloppe canonique reste stable), seul le contenu évolue. Cela permet de réordonner ou de substituer un filtre sans casser la chaîne.
┌───────┐ ┌───────┐ ┌─────────┐ ┌─────────┐ ┌──────┐
│ parse │──▶│ valid │──▶│ enrich │──▶│ trans- │──▶│ ship │
│ AS2 │ │ ate │ │ (GLN→ │ │ late │ │ │
│ EDI │ │ schema│ │ adresse)│ │ canon. │ │ to │
└───────┘ └───────┘ └─────────┘ └─────────┘ │ part │
│ │ │ │ │ ner │
▼ ▼ ▼ ▼ └──────┘
pipe pipe pipe pipe
(queue) (queue) (queue) (queue)
Each filter:
- one input pipe, one (or many) output pipes
- same message envelope contract
- stateless if possible (state in canonical message) Implémentation EDI
Le pipeline typique d'un INVOIC entrant :
# Pipeline EDIFACT INVOIC entrant
filters:
- id: sniff-format
in: edi.inbound.raw
out: edi.detected.edifact # autre filtre pour x12 / ubl
role: detect EDIFACT vs X12 vs UBL
- id: parse-edifact
in: edi.detected.edifact
out: edi.canonical.invoic.raw
role: parse UN/EDIFACT INVOIC D96A → JSON canonical
- id: schema-validate
in: edi.canonical.invoic.raw
out: edi.canonical.invoic.validated
fail: edi.deadletter.invalid
role: JSON-schema + business rules
- id: enrich-gln
in: edi.canonical.invoic.validated
out: edi.canonical.invoic.enriched
role: GLN→party master, GTIN→product master
- id: translate-erp
in: edi.canonical.invoic.enriched
out: erp.bookkeeping.invoice.in
role: canonical → SAP IDoc INVOIC02
Chaque filtre a son propre canal d'entrée et de sortie, sa propre
DLQ (edi.deadletter.*), sa propre observabilité. On
peut faire évoluer indépendamment : ajouter
anonymize-pii entre schema-validate et
enrich-gln, ou remplacer parse-edifact
par une nouvelle implémentation sans toucher au reste. La règle
d'or est que chaque filtre publie sur sa sortie un
message du même type canonique que celui qu'il a
consommé, enrichi ou allégé ; jamais un autre type. Cela
distingue les pipes-and-filters d'un workflow où chaque étage
parle son propre dialecte.
Parallélisme & ordre
Le parallélisme est natif : chaque filtre peut tourner en N
instances (competing consumers sur sa queue d'entrée). Mais cela
casse l'ordre. Si l'ordre compte (par exemple un ORDERS suivi
d'un ORDCHG sur la même PO), il faut partitionner par clé
(Kafka avec key = controlRef) ou imposer une
sériale dans le filtre concerné. Les autres filtres peuvent
rester parallèles.
Anti-patterns
- Filtre god-object. Un filtre qui parse, valide ET traduit. On retombe sur les couplages.
- Filtres qui partagent une base de données. Couplage par la donnée : un changement de schéma bloque deux filtres. Préférer le canonique passé en message.
- Pas de DLQ par étape. Si un message
échoue dans
enrich-gln, il ne doit pas remonter àparse-edifact; il doit aller en DLQ de l'étape, avec contexte. - Type de message qui change à chaque filtre. Si
la sortie de
parse-edifactest un objet, celle devalidateun autre, celle deenrichun troisième, on a perdu le contrat canonique — toute substitution devient impossible.
Patterns liés
- Message Channel — chaque pipe est un canal nommé.
- Message Translator — un filtre particulier qui change le schéma.
- Content Enricher — un filtre particulier qui ajoute des champs.
- Splitter et Aggregator — filtres à cardinalité non-1:1.
Sources
- Hohpe G., Woolf B. — Enterprise Integration Patterns, pattern Pipes and Filters (§70). enterpriseintegrationpatterns.com — Pipes and Filters
- Kernighan B., Plauger P. — Software Tools, Addison-Wesley, 1976. La formalisation pédagogique du concept de pipe UNIX dont s'inspire le pattern.
- Shaw M., Garlan D. — Software Architecture: Perspectives on an Emerging Discipline, Prentice Hall, 1996. Chapitre sur les styles d'architecture, dont le style pipes-and-filters.
- Apache Camel — la totalité du framework est organisée autour du pattern. Les routes Camel sont des pipelines pipes-and-filters. camel.apache.org — Pipeline