Turning Synchronous BFF Calls into Scalable Asynchronous Pipelines
This article shares a step‑by‑step experience of refactoring a finance‑BFF aggregation service from tightly coupled synchronous calls to a loosely coupled, fully asynchronous architecture, covering problem background, redesign goals, implementation details, Hystrix integration, monitoring, and practical usage examples.
Problem Background
Different financial products (funds, brokerage, insurance, bank wealth, etc.) need personalized recommendations for various channels and user groups. The BFF layer aggregates all data, relying on many downstream atomic services, making it hard to guarantee TP99 latency and availability.
Case Example
A typical recommendation interface depends on local product cache, algorithm service, product info service, position query, user tag service, coupon config, coupon claim, and many other services. Each product may require binding ten dynamic attributes, leading to (1~n)*10 I/O calls per request.
Before‑Refactor Flow and Issues
Flow diagram (omitted). Issues:
Strong coupling and synchronous dependencies across services.
Long call chain; a single unstable upstream service can cause total failure.
After‑Refactor Flow and Goals
New flow diagram (omitted). Goal: increase weak dependencies to enable early asynchronous loading, make components detachable for graceful degradation, and reduce overall failure risk.
New Issues After Initial Refactor
Logical decoupling is simple; the focus is on governance of async implementation.
Technical problems with @Async annotation:
Proliferation of @Async annotations across the project.
Unclear thread‑pool sharing leads to many custom pools.
Excessive nesting or ineffective @Async usage.
Redundant downgrade switch code.
Lack of unified request‑level cache despite partial support.
Thread‑pool context propagation issues.
No unified monitoring/alerting for thread‑pool status.
Overall Refactor Path
Focus on the IO client layer (e.g., com.xx.package.xxx.client) as the entry point for systematic governance.
Implementation Details
1. IO Call Abstract Template
The template standardizes and enhances IO calls, offering a default template and a cache‑enabled template. It declares attributes such as thread‑pool group, request group, timeout, cache, circuit‑breaker, and downgrade settings.
2. Delegate Proxy
The delegate bridges the template and the actual IO implementation, handling pre‑call, post‑call, and fallback logic.
3. Executor Selection
Hystrix is chosen as the primary framework (with Resilience4j as a backup) to provide thread‑pool isolation, circuit‑breaker, and retry capabilities.
4. Hystrix Dynamic Configuration
Extend concrete.PropertiesNotifier to listen for Hystrix‑prefixed keys from the configuration center.
Implement HystrixDynamicProperties to replace the default properties implementation.
5. Hystrix Thread‑Pool Context Propagation
Override HystrixConcurrencyStrategy#wrapCallable to capture and transfer MDC context to worker threads.
6. Thread‑Pool Monitoring and Alerting
Three custom components are introduced:
ThreadPoolMonitorHandler – periodically collects and reports metrics.
ThreadPoolEndpointMetrics – defines the data model (instance, pool type, group, core parameters).
AbstractThreadPoolMetricsPublisher – abstracts the reporting channel (Micrometer, PFinder, UMP, etc.).
Hystrix status collection enables multi‑dimensional monitoring (data center, group, instance, pool type, name).
7. Unified Await‑Future Utility
A helper class simplifies awaiting List<Future<T>> or Map<String,Future<T>> results, since Hystrix does not natively support CompletableFuture.
8. Additional Small Features
Custom trace‑ID propagation for sub‑threads and non‑SGM environments.
JSF filter for cross‑application request‑ID transmission.
Dynamic logging switches for JSF providers/consumers.
Automatic reporting of IO calls and fallbacks to UMP.
Daily Usage Examples
1. Simple IO Call Wrapper
Inherit the abstract template to gain async callback support without redefining thread‑pool groups.
2. IO Call with Request‑Level Circuit‑Breaker
Implement FallbackRequest to allow service‑level fallback while keeping other requests unaffected.
3. IO Call with Request‑Level Cache, Service‑Level Circuit‑Breaker, and Dedicated Thread‑Pool
Combine cache template, Hystrix configuration, and custom thread‑pool to achieve full feature set.
4. Upper‑Layer Invocation and Real‑World Effect
Convert a product list into an asynchronous attribute‑binding task.
Use the await utility to synchronize results.
Achieve transparent thread‑pool management, circuit‑breaker, downgrade, and caching without changing upper‑layer code.
Adjust timeout or circuit‑breaker thresholds in real time via PFinder monitoring.
The article concludes by reflecting on how systematic code‑level governance, based on existing frameworks, can significantly improve interface performance and reliability.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
