Event-driven architecture with a Kafka backbone
Domain events, outbox, exactly-once posting and the observability you need when causality spans services. By Sanjeev Kumar, Application Architect.
When events help
Event-driven design pays off when multiple capabilities must react to the same business fact without a central orchestrator becoming a bottleneck — posting notifications, inventory signals, order pipelines. The Metro Cash & Carry engagement is one example of time-sensitive, event-driven order flow in the portfolio. On high-volume banking platforms, the same idea shows up as a Kafka event backbone between strangler-extracted services — see the financial transaction platform case study.
Model the business event, not the table row
Useful topics carry business facts (“payment posted”, “order accepted”) with stable schemas, not raw CDC dumps dumped onto consumers. Schema registry and compatibility rules prevent silent breakage. Partition keys should preserve ordering where the domain needs it (for example per account) without creating hot partitions. That discipline matters more than broker feature checklists.
Exactly-once is a business outcome
Brokers offer strong building blocks; “exactly-once posting” in a bank still requires idempotent consumers, dedupe stores and a transactional outbox on the producer side. Broker-level exactly-once does not extend across a database write and an external publish — the dual-write problem stays. Collapsing both into one local commit via an outbox table, then relaying to Kafka, is the pattern used on the financial platform modernisation programme. Retries without idempotency keys create duplicate money movements.
Choreography, orchestration and saga
Choreography keeps services loosely coupled; orchestration is clearer when the workflow is long-lived and must be steered. Sagas compensate rather than distributed-lock the world. Pick the style that operators can explain at 2 a.m. Document the happy path and the compensating path in ADRs. Prefer asynchronous workflows when the business allows eventual consistency; when it does not, make the consistency boundary explicit at the service edge.
Consumer design and failure paths
Prefer small, purposeful consumer groups with clear lag SLOs. Poison messages need a dead-letter path and an operator runbook — infinite retry is not resilience. Back-pressure into upstream producers when lag breaches thresholds. At-least-once delivery is the common case: consumers must tolerate duplicates even when producers are careful.
Observability across hops
Propagate correlation IDs from the edge to message headers to downstream logs. Without that, event-driven systems become un-debuggable. Lag, error rate and compensation rate are first-class SLIs. Pair with microservices and Java notes for service boundaries and Spring Boot implementation habits.