Not tutorials. Not clones. These are domain-specific backend systems where I made real architectural decisions — and learned from the ones that didn't work.
A ledger-based loan management system where every financial transaction creates an immutable,
hash-chained entry — the same principle banks use for audit trails. Loans go through a full lifecycle:
application → async credit check → approval → Kafka-driven disbursal → EMI generation → repayment with
late penalty calculation → foreclosure.
System Flow
User applies for loan
│
├── RiskAssessmentEngine runs async credit check on background ThreadPool
│ └── CompletableFuture + custom ThreadPoolExecutor (5 core, 20 max, CallerRunsPolicy)
│
├── Admin approves → disburseLoan() publishes DisbursalEvent to Kafka
│ └── DisbursalConsumer: credits wallet + generates installment schedule
│
├── Each payment: SELECT FOR UPDATE on installment row (pessimistic lock)
│ ├── Wallet debit with BigDecimal precision
│ ├── Late penalty = per-day rate × days overdue (rate varies by credit score)
│ └── LedgerEntry created with SHA-256 hash chain (previousHash → currentHash)
│
└── Every ledger write triggers: score recalculation + fraud detection
└── FraudDetectionService: sliding-window rate limiter (ConcurrentHashMap + ArrayDeque)
Spring Boot 3.2
Spring Kafka
JPA + PostgreSQL
Spring Security + JWT
Spring AOP
Caffeine Cache
Spring Actuator
BigDecimal
Centralized Audit Logging — Spring AOP
Custom @Auditing annotation + @Around aspect intercepts any annotated service method
│
├── Captures: authenticated userId (from SecurityContext), client IP (X-Forwarded-For aware)
├── Measures: method execution time via System.currentTimeMillis() delta
├── Records: method arguments, action name, SUCCESS/FAILED status
└── Persists to AuditLog entity — every loan approval, wallet debit, disbursal is traced
Result: zero audit code inside business logic. Add @Auditing(action = "LOAN_DISBURSED") and it's logged.
Kafka Event Pipeline
LedgerService.record() saves hash-chained entry → publishes LedgerCreatedEvent
│
├── @EventListener + @Async → recalculates credit score (repayment +20, deposit +5)
├── Forwards to Kafka "ledger-topic" → LedgerConsumer (downstream analytics)
└── FraudDetectionService: sliding-window rate limiter
└── ConcurrentHashMap + ArrayDeque: if >10 txns in 5 sec → publishes to "fraud-topic"
"disbursal-topic": LoanService publishes DisbursalEvent on approval
└── DisbursalConsumer: wallet credit + installment schedule generation — fully async
- Interest rate adjusts by credit score (1%–4% annual rate tiers) — EMI formula with proper amortization math and last-month rounding correction
- Every wallet operation uses SELECT FOR UPDATE (pessimistic locking) to prevent double-spend on concurrent requests
- LedgerIntegrityService validates the entire SHA-256 hash chain on demand — wired into Spring Actuator custom HealthIndicator
- Foreclosure: remaining principal + 2% fee, cancel all pending installments, close loan — single @Transactional boundary
- All financial math uses BigDecimal with explicit RoundingMode — no floating point anywhere in the money path
View source on GitHub ↗
A real-time appointment and queue management system for clinics. Patients book slots,
get token numbers, and see live queue updates via WebSocket. The interesting part: a smart
shuffling engine that promotes checked-in patients over no-shows in real time.
Queue Intelligence
Patient checks in at reception
│
├── QueueShufflingService acquires ReentrantLock
│ ├── Scans today's BOOKED appointments sorted by token
│ ├── Finds "holes": absent patient ahead of present patient
│ └── Swaps token numbers atomically, then releases lock
│
├── WebSocket broadcast via STOMP to /topic/queue/{doctorId}
│ └── All connected clients see updated queue positions instantly
│
└── Doctor calls next → tracks consultation duration → calculates
average wait time from historical data (totalDuration / totalConsultations)
Spring Boot 3.2
WebSocket + STOMP
Spring Security + JWT
JPA + PostgreSQL
Spring Mail
Caffeine Cache
Swagger/OpenAPI
- Role-based access: ADMIN manages doctors, DOCTOR calls next patient, PATIENT books and tracks queue position
- Async email notifications on booking confirmation using Spring's @Async with custom thread pool
- Insurance verification runs asynchronously after booking — doesn't block the HTTP response
- Daily scheduled task auto-cancels stale BOOKED appointments from previous days
- Wait time estimation: remaining appointments × average consultation time + time left for current patient
- Doctor performance tracking: total consultations, total duration, calculated per-patient average
View source on GitHub ↗
A Kafka-driven notification microservice built to decouple delivery from the calling system entirely.
A single REST call to the gateway dispatches to one, several, or all three channels — Email, SMS,
and Firebase push — without the caller knowing or caring which provider handles it. Designed for
multi-tenant use: each tenant gets its own Firebase app, initialized on-demand and cached.
Request → Kafka → Channel Services
POST /api/v1/notify (notificationType: EMAIL | SMS | PUSH | ALL)
│
├── NotificationGatewayService validates channel-specific fields
│ └── email required for EMAIL, phone for SMS, fcmToken for PUSH
│
├── NotificationEventPublisher routes to Kafka topic(s)
│ ├── email-notification-topic → brevo-email-service
│ ├── sms-notification-topic → sms-service (2Factor API)
│ └── push-notification-topic → firebase-fcm-service
│
└── Gateway returns 202 ACCEPTED immediately — caller never waits on delivery
Kafka Producer — Reliability Config
Producer keyed by tenantId — ordering guarantee within a tenant's messages
│
├── acks=all: broker waits for all in-sync replicas before confirming
├── enable.idempotence=true: exactly-once semantics, no duplicate messages on retry
└── retries=3 with CompletableFuture callback — failures logged with topic + partition + tenantId
Consumer Error Handling — All Three Services
AckMode: MANUAL_IMMEDIATE — consumer only commits offset after confirmed delivery
│
├── DefaultErrorHandler with FixedBackOff: 3 retries × 2 s before dead-lettering
├── Delivery failure re-throws → Kafka retries without committing the offset
└── Each service independently recoverable — email outage doesn't affect SMS or push
Multi-Tenant Firebase — Dynamic App Management
Each tenant has a registered Firebase service-account JSON stored in PostgreSQL
│
├── FirebaseAppManager: @Cacheable("firebaseApps") keyed by tenantId
│ └── Caffeine cache: max 200 tenants, evicted after 60 min of inactivity
│
├── On cache miss: loads JSON from DB → GoogleCredentials → FirebaseApp.initializeApp()
│ └── Checks FirebaseApp.getInstance() first — guards against double-init on warm restart
└── Each push delivery: FirebaseMessaging.getInstance(app).send() with correct tenant app
Spring Boot 3.2
Spring Kafka
Firebase Admin SDK
Brevo (Sendinblue) API
2Factor SMS API
Caffeine Cache
PostgreSQL
Docker Compose
Confluent Kafka 7.5
- Four independent Spring Boot services — gateway, email, SMS, push — each deployable and scalable on its own
- Tenant isolation at the Kafka level: tenantId is the partition key, so one tenant's burst doesn't reorder another's messages
- Brevo transactional email: constructs SendSmtpEmail with sender, recipient, subject, and HTML body; re-throws on failure so the consumer retries
- SMS via 2Factor REST API — phone sanitized to E.164 format, message URL-encoded before dispatch
- Firebase service-account credentials never hardcoded — fetched per tenant from DB, deserialized into GoogleCredentials at runtime
- Manual offset commit strategy: zero risk of losing an undelivered notification — the message stays in Kafka until delivery is confirmed