Architecture
Not just a diagram gallery — the reasoning behind why the projects and case studies on this site are shaped the way they are, illustrated where a picture clarifies the shape faster than prose.
Event-driven integration over point-to-point calls
RelayHub treats integration as ingest → validate → transform → deliver, with Kafka decoupling producers from consumers. A slow or unavailable downstream target degrades that target's delivery, not the whole pipeline.
Retry and DLQ as first-class outcomes, not error handling as an afterthought
A delivery failure is an expected outcome, not an exception to route around. Bounded retries, then a dead-letter path with enough context to diagnose and replay, mean a failure is always something a human can act on later — not silently dropped or retried forever.
Idempotency and ordering where they actually matter
Concurrency without an ordering guarantee is how the race condition in “A Race Condition in Distributed Delivery Processing” (Case Studies) happened. Where two related events can race, per-entity ordering (not a global lock) is usually the cheapest fix that preserves throughput.
Observability as part of the design, not bolted on after
RelayHub's admin console is backed by Prometheus metrics fed by continuous synthetic traffic (relayhub-demo-systems), so throughput, latency, and failure rate are visible even without live production integrations.
Evidence over simulation, once evidence is actually available
This site's RelayHub Live Monitoring page used to be a client-side mock of RelayHub's pipeline; it now reads relayhub-java's real, public delivery/DLQ/metrics endpoints directly, scoped by CORS to exactly this origin. A simulation is worth building only until the real thing can be shown safely — once it can, showing the real thing is strictly more convincing.
Batch vs. real-time is a deliberate choice, not a default
“Legacy State vs. New Configuration in Batch Processing” (Case Studies) is a reminder that a batch job's assumptions about record shape have to account for records created under an older configuration — a schema or config change isn't finished until existing records are accounted for, not just new ones.