Relay MeshAn event-driven integration hub
A reference architecture for synchronizing orders across e-commerce, ERP, CRM and a payment provider with safe retries, no duplicate effects and auditable reprocessing.
- Discipline
- Custom Software
- Technologies
- TypeScript · PostgreSQL · Message broker · OpenTelemetry
- Demonstrates
- Transactional outbox
- Sagas with compensation
- End-to-end idempotency
- Operable without engineers
The challenge
- 01
Webhooks arrive duplicated, out of order, or not at all. External systems go down for minutes at a time.
- 02
Writing to the database and publishing a message are two operations; if one fails, systems drift apart.
- 03
When something breaks, operations needs to see what happened and replay it without paging an engineer.
Constraints
- Legacy systems
- No duplicate effects
- Operable by non-engineers
Architecture
Components
- E-commerce
- Order source via webhook.
- Payments
- Payment confirmations via webhook.
- Verified inbox
- Verifies HMAC signatures and drops duplicates by event ID.
- Order saga
- A state machine with a compensation for every step.
- Outbox
- Messages written in the same transaction as state.
- ERP
- Receives orders and invoices with an idempotency key.
- CRM
- Updates the customer timeline.
- Dead letters
- Permanent failures with full context and a replay button.
Engineering decisions
D1Transactional outbox
- Choice
- Order state and the message to publish are written in one transaction; a relay publishes afterwards.
- Rejected alternative
- Writing to the database and then publishing to the broker.
- Why
- Dual writes fail partially. With an outbox, either both happen or neither does.
D2At-least-once + idempotent consumers
- Choice
- Every external call carries an idempotency key derived from the source event.
- Rejected alternative
- Chasing exactly-once delivery in the transport.
- Why
- Exactly-once on the network doesn't exist in practice. Exactly-once in effect does, through idempotency.
D3Saga instead of a distributed transaction
- Choice
- Every step has an explicit compensation (release reservation, void invoice).
- Rejected alternative
- Two-phase commit across systems.
- Why
- ERPs and payment providers don't take part in 2PC. Compensations are what those systems actually offer.
D4Replay as a feature
- Choice
- A dead-letter queue with context, reason and replay from the UI.
- Rejected alternative
- Logs for an engineer to dig through.
- Why
- Integration failures are routine. Operators need to resolve them without the development team.
Implementation details
Webhooks enter through an endpoint that verifies the signature, stores the raw event in the inbox keyed by its external ID and answers quickly. Processing happens later, off the request path.
The saga advances one step at a time and writes the new state and outgoing messages in the same transaction. A relay reads the outbox in order and publishes with exponential retries.
Every message carries a trace ID from the originating webhook, so the operations view shows an order's complete history as one timeline.
export async function advance(orderId: string, event: DomainEvent) {
await db.transaction(async (tx) => {
const saga = await tx.sagas.lockById(orderId); // SELECT … FOR UPDATE
const step = saga.transition(event); // pure: returns next state + commands
await tx.sagas.save(saga.id, step.state);
for (const command of step.commands) {
await tx.outbox.insert({
topic: command.topic,
payload: command.payload,
// Same source event => same key => the ERP applies it once.
idempotencyKey: `${event.id}:${command.topic}`,
traceId: event.traceId,
});
}
});
}Outcomes
Not applicable. As an internal demonstration there are no verified production metrics to report, and we don't publish estimates dressed up as results.
Facing a similar problem?
Tell us the context. We discuss architecture before any commercial proposal.
Next studyLedgerlineContract intelligence with evaluated retrieval