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.

Event Sourcing

Store the complete history of business facts, not current state — and accept that state is just a projection of the log.

Problem

EDI naturally produces a stream of business events (ORDERS, ORDRSP, DESADV, INVOIC, REMADV) chained over time. A system modelled as CRUD ("update the order to shipped") loses this fine-grained traceability and makes audit, replay and incident investigation difficult.

Forces

  • Audit is mandatory in EDI — 10-year fiscal proof, customs compliance, regulatory traceability. Storing the raw history is a feature, not a burden.
  • Business events are the truth. State is just a local cache, reconstructible at will.
  • Replay enables model evolution. When the schema changes, we replay history to rebuild the new projection.
  • Storage cost is low. A compressed business-event log costs an order of magnitude less than an equivalent denormalised data lake.

Solution

Every business fact derived from an inbound or outbound EDI message is persisted as an immutable event in an append-only log (Kafka, EventStoreDB, append-only PostgreSQL table). The current state of an entity (order, shipment, invoice) is computed by replaying its events. Projections are materialised into read-side tables (read models) optimised per use case — operations console, partner API, BI data lake each draw their view from the same source of truth.

plaintext order-event-log.txt
# event-log topic
2026-05-14T08:31Z  OrderReceived            { orderId: ORD789, lines: [...] }
2026-05-14T08:42Z  OrderAcknowledged        { orderId: ORD789, status: "accepted" }
2026-05-15T07:11Z  ShipmentDispatched       { orderId: ORD789, asn: ASN9001 }
2026-05-15T15:02Z  GoodsReceived            { orderId: ORD789, asn: ASN9001 }
2026-05-15T17:30Z  InvoiceIssued            { orderId: ORD789, invoice: INV-7811 }
2026-05-20T09:00Z  PaymentRemitted          { orderId: ORD789, invoice: INV-7811, amount: 1240.00 }

EDI implementation

Typical EDI hub case: every inbound message is translated into a business event (OrderReceived, ShipmentDispatched, InvoiceIssued, PaymentRemitted) published to a Kafka topic edi.events.orders partitioned by orderId. The ERP projector consumes it and materialises an orders table; the audit projector feeds a fiscal index; the partner projector publishes a JSON status to a REST API. On incident ("I lost an order"), replaying the topic reconstructs the state at any point in time — impossible with a classic CRUD.

Anti-patterns

  • Storing state in the event. The event must carry the fact, not the projection. Embedding full state defeats the benefit.
  • No event versioning. When the schema changes after 18 months, replay becomes impossible: an eventVersion is required from sprint one.
  • Aggressive compaction. Deleting old events loses historic audit ability — forbidden in fiscal EDI.
  • Projector / event store coupling. The projector must be addable, removable, replayable without impact on the source of truth.
  • Event Sourcing everywhere. For a small static partner directory, CRUD remains simpler. ES applies to entities with a rich lifecycle, not to reference tables.
  • CQRS — Event Sourcing is almost always paired with CQRS.
  • Message Store — the EIP equivalent applied to the journal.
  • Change Data Capture — the non-intrusive alternative for legacy systems that cannot be rewritten.

Sources