CASE 03 / ENGINEERING IN DETAIL
Outbound Calling Platform Modernization
The monolith needed service decomposition alongside fixes for lock contention, timeouts, and out-of-memory failures.
Context and constraints
As manual and automated outbound calling evolved, the monolith encountered lock contention, timeouts, and out-of-memory failures. I led service decomposition and worked on JVM settings, SQL, caching, and hot/cold data separation. The system also uses parallel agent-tag queries. The migration had to preserve business rules and coordinate scheduling, configuration, and releases.
Core workflow
This diagram is a simplified view of the technical approach.
- 01 / Calling requestsManual and automated outbound calls
- 02 / Rule validationCall restrictions, blocklists, and limits
- 03 / Service coordinationBusiness calls and task management
- 04 / Data accessCaching, queries, and hot/cold separation
Design decisions and implementation
Split services while preserving business rules
ChallengeResponsibilities within the monolith affected one another, allowing pressure in one area to become a wider runtime problem.
ApproachLed the move from a monolith to microservices and introduced XXL-JOB and Apollo for scheduling and configuration. A dedicated outbound validation service handles call restrictions, blocklists, and phone-number and frequency limits.
ReasoningOutbound validation and business execution can be maintained separately. Rules, error responses, and restrictions still need to be checked after the split.
Query tags in parallel and retain partial results
ChallengeAgent-facing tag lookups depended on multiple downstream queries. Sequential waiting and individual failures could both affect result assembly.
ApproachThe tag-query path uses a bounded thread pool and CompletableFuture to fetch downstream results in parallel, retaining available information when an individual query fails.
ReasoningQueries can run together, and available information can survive an individual failure. Pool capacity limits concurrency; slow dependencies still need separate checks.
Address memory, query access, and data volume separately
ChallengeLock contention, slow queries, and memory pressure overlapped, so a single configuration adjustment could not address every cause.
ApproachTuned JVM settings for concurrent access, improved SQL indexes and Redis caching, and separated hot and cold data to address runtime behavior, query access, and data volume individually.
ReasoningJVM settings, indexes, caching, and data separation act in different places. Each needs observation under comparable loads; one faster response does not establish an overall improvement.
What the design addresses
Delivered service decomposition and scheduling and configuration integrations, with JVM, SQL, caching, and hot/cold data improvements.
Validation focus
- Do business rules, error responses, and restrictions retain their semantics after service decomposition?
- Do thread usage and partial-result handling behave as intended when downstream services are slow or fail?
- Are JVM, SQL, cache, and hot/cold data changes evaluated under comparable loads?
My contribution
Led the monolith-to-microservices migration, covering technical design, core development, and team delivery. Worked with product, QA, and operations on releases and production issues.