← Back to works

CASE STUDY

Nine microservices, two brokers, 100K+ requests a day

Event-driven messaging over Kafka and Amazon SQS, with service discovery, an API gateway and circuit breakers holding 99.99% uptime.

Role

Full Stack Developer — service design and messaging

Project

QSS Technosoft · Sep 2022 – present

Spring BootApache KafkaAmazon SQSDockerSpring Security

Containerised microservices

9

Requests handled

100K+ / day

Uptime

99.99%

Throughput gain

40%

Latency reduction

60%

CONTEXT

A multi-tenant SaaS platform where a single user action fans out into work that other parts of the system care about — check-ins, queue position updates, notifications and calendar sync. Each organisation's data has to stay separate on shared infrastructure.

THE PROBLEM

Calling every interested service synchronously made the request path as slow as its slowest dependency and as fragile as its least reliable one. Adding a new consumer of an existing event meant editing and redeploying the producer.

HOW IT WORKS

Architecture

Producers publish once; consumers subscribe independently. A circuit breaker keeps one failing downstream from taking the request path with it.

Producer servicepublishes oncemessagingKafka topicretained · replayableAmazon SQSpoint-to-point workNotificationsconsumer serviceQueue updatesconsumer serviceCalendar syncconsumer serviceTenant storeorg-scopedPUBLISH ONCE — NO CALLER WAITSSUBSCRIBE INDEPENDENTLYREQUESTS HANDLED100K+ / dayUPTIME99.99%circuit open · failing fast

TRADE-OFFS

Decisions

What was chosen, what it was chosen over, and why.

Event-driven messaging between services

ConsideredSynchronous REST calls between services

WhyA synchronous chain makes every caller wait on every callee and couples deploys together. Publishing an event lets a new consumer subscribe without the producer knowing it exists, and keeps the user-facing request fast.

Kafka and Amazon SQS side by side

ConsideredOne broker for everything

WhyThey answer different questions. A retained, replayable log suits events several services consume and may need to reprocess; a managed queue suits point-to-point work that just needs to be done once, without the operational overhead.

Circuit breakers on outbound calls

ConsideredRetries and timeouts alone

WhyRetries against an already-failing dependency add load to the thing that is falling over. Breaking the circuit fails fast and lets the rest of the platform stay inside its uptime budget while the dependency recovers.

Service discovery behind an API gateway

ConsideredHard-coded service addresses per environment

WhyInstances move, scale and get replaced. One entry point plus discovery keeps routing, authentication and TLS termination in a single place instead of duplicated across nine services.

RBAC with tenant scoping in the security layer

ConsideredPer-endpoint permission checks in controllers

WhyMulti-tenant separation enforced per endpoint is one missed check away from a cross-tenant leak. Putting the check in the security layer with JWT, MFA and OAuth 2.0 makes the safe path the default.

IN HINDSIGHT

What I'd do differently

  • Agree the event schema and its versioning rules before the second consumer arrives, not after a payload change breaks one.
  • Alert on consumer lag against a stated target rather than reading throughput dashboards after a report comes in.
  • Give each service its own uptime target — one platform-wide number hides which service is actually degrading.
Footer

Connect with me on social media.

linkedingithub

Designed and Developed with ❤️ by Shubham

Send me an email!
Let's chat on WhatsApp!