This site is in English. Use your browser's built-in translate feature to read it in your language.

Dynamic Router Pattern

Overview

The Dynamic Router pattern is an advanced message routing capability in enterprise integration architectures that determines message destinations at runtime based on configurable rules, current system state, and dynamic conditions. Unlike static routing patterns that follow predetermined paths, the Dynamic Router acts as an intelligent traffic controller that analyzes messages, evaluates real-time conditions, and selects the most appropriate destination from a set of possible endpoints. This pattern enables sophisticated routing decisions that adapt to changing business requirements, system loads, and operational conditions without requiring system redeployment.

Theoretical Foundation

The Dynamic Router pattern is rooted in adaptive routing algorithms and runtime decision-making systems, specifically addressing the challenge of flexible message distribution in complex, evolving enterprise environments. The pattern incorporates principles from artificial intelligence, rule-based systems, and real-time analytics to enable intelligent routing decisions that consider multiple factors including message content, system state, business rules, and performance characteristics.

Core Principles

1. Runtime Route Determination

The Dynamic Router makes routing decisions at message processing time: - Context-sensitive routing - considering current system state, load conditions, and availability - Content-aware routing - analyzing message payload, headers, and metadata for routing decisions - Rule-based routing - applying configurable business rules and logic to determine destinations - Adaptive routing - learning from historical patterns and performance data to optimize decisions

2. Multi-Factor Decision Making

The pattern evaluates multiple criteria simultaneously: - Message characteristics - type, priority, size, content, and source system - System conditions - endpoint availability, load levels, performance metrics, and capacity - Business context - time of day, user preferences, geographic location, and organizational rules - Historical patterns - past routing success rates, performance trends, and failure patterns

3. Configurable Routing Logic

The Dynamic Router supports flexible routing approaches: - Rule-based configuration - externalized rules that can be updated without deployment - Algorithmic routing - using algorithms for load balancing, geographic optimization, or performance maximization - Machine learning integration - incorporating ML models for predictive routing decisions - Hybrid approaches - combining multiple routing strategies for optimal results

4. Real-Time Adaptation

The pattern enables continuous optimization: - Performance monitoring - tracking routing success rates and performance metrics - Automatic adjustment - adapting routing decisions based on real-time feedback - Failover handling - dynamically routing around failed or degraded endpoints - Learning capabilities - improving routing decisions over time through pattern analysis

Why Dynamic Routing is Essential in Integration Architecture

1. Operational Flexibility

In dynamic business environments, the Dynamic Router provides: - Business agility - adapting to changing business requirements without system changes - Operational efficiency - optimizing resource utilization through intelligent routing - Scalability support - distributing load across available resources based on capacity - Maintenance windows - routing around systems undergoing maintenance or updates

2. Performance Optimization

For achieving optimal system performance: - Load distribution - dynamically balancing load across multiple endpoints - Latency minimization - routing to geographically or network-optimal destinations - Resource optimization - considering endpoint capacity and current utilization - Throughput maximization - selecting routes that provide the best overall throughput

3. Resilience and Reliability

In mission-critical systems: - Automatic failover - routing around failed or degraded components - Circuit breaker integration - respecting circuit breaker states in routing decisions - Health-aware routing - considering endpoint health in routing calculations - Disaster recovery - automatically routing to backup systems during outages

4. Business Intelligence Integration

For data-driven decision making: - Analytics-driven routing - using business intelligence to inform routing decisions - Customer segmentation - routing based on customer characteristics and preferences - Geographic optimization - routing based on geographic proximity and regulations - Time-based routing - adapting routing behavior based on time patterns and schedules

Benefits in Integration Contexts

1. Enhanced Adaptability

2. Improved Performance

3. Operational Excellence

4. Business Value

Integration Architecture Applications

1. Microservices Architecture

Dynamic Routing in microservices provides: - Service discovery integration - dynamically routing to available service instances - Version management - routing to appropriate service versions based on client requirements - Canary deployments - gradually routing traffic to new service versions - A/B testing support - routing subsets of traffic to different service implementations

2. Cloud and Hybrid Deployments

In cloud environments: - Multi-cloud routing - distributing requests across multiple cloud providers - Cost optimization - routing to most cost-effective cloud resources - Geographic distribution - routing to nearest available cloud regions - Capacity scaling - routing based on auto-scaling and capacity availability

3. Enterprise Application Integration

For connecting enterprise systems: - System availability routing - routing around systems in maintenance or failure states - Data residency compliance - ensuring data is routed according to geographic regulations - Priority-based routing - routing high-priority messages to premium service endpoints - Business hours routing - adapting routing behavior based on business operational hours

4. API Management and Gateways

In API gateway implementations: - Backend versioning - routing to appropriate API backend versions - Rate limiting integration - routing around rate-limited endpoints - Authentication-based routing - routing based on user authentication and authorization - Tenant isolation - routing multi-tenant requests to appropriate isolated resources

Implementation Patterns

1. Rule-Based Dynamic Router

// Dynamic router using configurable business rules
@Component
public class RuleBasedDynamicRouter {

    @Autowired
    private RoutingRuleEngine ruleEngine;

    @Autowired
    private EndpointRegistry endpointRegistry;

    @Autowired
    private SystemMonitoringService monitoringService;

    public RoutingDecision routeMessage(MessageContext messageContext) {
        // Gather current context information
        RoutingContext context = buildRoutingContext(messageContext);

        // Apply routing rules in priority order
        List<RoutingRule> applicableRules = ruleEngine.getApplicableRules(context);

        for (RoutingRule rule : applicableRules) {
            RoutingDecision decision = rule.evaluate(context);
            if (decision.isDefinitive()) {
                // Validate the selected endpoint
                if (isEndpointAvailable(decision.getTargetEndpoint(), context)) {
                    return decision;
                }
                // If endpoint not available, continue to next rule
            }
        }

        // Fallback to default routing
        return createDefaultRoutingDecision(context);
    }

    private RoutingContext buildRoutingContext(MessageContext messageContext) {
        return RoutingContext.builder()
            .message(messageContext.getMessage())
            .headers(messageContext.getHeaders())
            .timestamp(Instant.now())
            .systemLoad(monitoringService.getCurrentSystemLoad())
            .availableEndpoints(endpointRegistry.getAvailableEndpoints())
            .businessContext(extractBusinessContext(messageContext))
            .build();
    }

    private boolean isEndpointAvailable(String endpoint, RoutingContext context) {
        EndpointHealth health = monitoringService.getEndpointHealth(endpoint);

        return health.isAvailable() && 
               health.getResponseTime() < context.getMaxAcceptableLatency() &&
               health.getCurrentLoad() < health.getMaxCapacity() * 0.8; // 80% threshold
    }
}

@Service
public class RoutingRuleEngine {

    private final List<RoutingRule> rules = new ArrayList<>();

    @PostConstruct
    public void initializeRules() {
        rules.add(new PriorityBasedRoutingRule());
        rules.add(new MessageTypeRoutingRule());
        rules.add(new LoadBalancedRoutingRule());
        rules.add(new GeographicRoutingRule());
        rules.add(new TimeBasedRoutingRule());
        rules.add(new DefaultRoutingRule());

        // Sort by priority
        rules.sort(Comparator.comparing(RoutingRule::getPriority));
    }

    public List<RoutingRule> getApplicableRules(RoutingContext context) {
        return rules.stream()
            .filter(rule -> rule.isApplicable(context))
            .collect(Collectors.toList());
    }
}

// Example routing rule implementation
public class PriorityBasedRoutingRule implements RoutingRule {

    @Override
    public boolean isApplicable(RoutingContext context) {
        return context.getHeaders().containsKey("priority");
    }

    @Override
    public RoutingDecision evaluate(RoutingContext context) {
        String priority = (String) context.getHeaders().get("priority");

        String targetEndpoint = switch (priority.toUpperCase()) {
            case "HIGH" -> selectHighPriorityEndpoint(context);
            case "MEDIUM" -> selectMediumPriorityEndpoint(context);
            case "LOW" -> selectLowPriorityEndpoint(context);
            default -> null;
        };

        if (targetEndpoint != null) {
            return RoutingDecision.builder()
                .targetEndpoint(targetEndpoint)
                .isDefinitive(true)
                .reason("Priority-based routing: " + priority)
                .confidence(0.9)
                .build();
        }

        return RoutingDecision.notDefinitive();
    }

    private String selectHighPriorityEndpoint(RoutingContext context) {
        return context.getAvailableEndpoints().stream()
            .filter(endpoint -> endpoint.getTier().equals("PREMIUM"))
            .filter(endpoint -> endpoint.getCurrentLoad() < endpoint.getCapacity() * 0.5)
            .findFirst()
            .map(EndpointInfo::getId)
            .orElse(null);
    }
}

2. Performance-Aware Dynamic Router

// Dynamic router that considers performance characteristics
@Component
public class PerformanceAwareDynamicRouter {

    @Autowired
    private PerformanceMetricsService metricsService;

    @Autowired
    private RoutingAlgorithm routingAlgorithm;

    public RoutingDecision routeBasedOnPerformance(MessageContext messageContext, 
                                                 List<String> candidateEndpoints) {

        Map<String, PerformanceMetrics> endpointMetrics = new HashMap<>();

        // Gather performance data for candidate endpoints
        for (String endpoint : candidateEndpoints) {
            PerformanceMetrics metrics = metricsService.getRecentMetrics(endpoint);
            if (metrics.isHealthy()) {
                endpointMetrics.put(endpoint, metrics);
            }
        }

        if (endpointMetrics.isEmpty()) {
            return RoutingDecision.failure("No healthy endpoints available");
        }

        // Apply performance-based routing algorithm
        String selectedEndpoint = routingAlgorithm.selectOptimalEndpoint(
            endpointMetrics, 
            messageContext.getRequirements()
        );

        return RoutingDecision.builder()
            .targetEndpoint(selectedEndpoint)
            .isDefinitive(true)
            .reason("Performance-optimized routing")
            .metrics(endpointMetrics.get(selectedEndpoint))
            .build();
    }
}

@Service
public class WeightedPerformanceRoutingAlgorithm implements RoutingAlgorithm {

    public String selectOptimalEndpoint(Map<String, PerformanceMetrics> endpointMetrics,
                                      MessageRequirements requirements) {

        Map<String, Double> endpointScores = new HashMap<>();

        for (Map.Entry<String, PerformanceMetrics> entry : endpointMetrics.entrySet()) {
            String endpoint = entry.getKey();
            PerformanceMetrics metrics = entry.getValue();

            double score = calculatePerformanceScore(metrics, requirements);
            endpointScores.put(endpoint, score);
        }

        return endpointScores.entrySet().stream()
            .max(Map.Entry.comparingByValue())
            .map(Map.Entry::getKey)
            .orElse(null);
    }

    private double calculatePerformanceScore(PerformanceMetrics metrics, 
                                           MessageRequirements requirements) {
        double latencyScore = calculateLatencyScore(metrics.getAverageLatency(), 
                                                  requirements.getMaxLatency());
        double throughputScore = calculateThroughputScore(metrics.getThroughput(), 
                                                        requirements.getMinThroughput());
        double availabilityScore = metrics.getAvailability();
        double loadScore = calculateLoadScore(metrics.getCurrentLoad(), 
                                            metrics.getMaxCapacity());

        // Weighted scoring based on requirements
        return (latencyScore * requirements.getLatencyWeight()) +
               (throughputScore * requirements.getThroughputWeight()) +
               (availabilityScore * requirements.getAvailabilityWeight()) +
               (loadScore * requirements.getLoadWeight());
    }

    private double calculateLatencyScore(Duration averageLatency, Duration maxLatency) {
        if (averageLatency.compareTo(maxLatency) >= 0) {
            return 0.0;
        }

        double ratio = (double) averageLatency.toMillis() / maxLatency.toMillis();
        return Math.max(0.0, 1.0 - ratio);
    }

    private double calculateLoadScore(int currentLoad, int maxCapacity) {
        double loadRatio = (double) currentLoad / maxCapacity;

        if (loadRatio >= 0.9) return 0.1; // Almost at capacity
        if (loadRatio >= 0.7) return 0.5; // High load
        if (loadRatio >= 0.5) return 0.8; // Medium load
        return 1.0; // Low load
    }
}

3. Machine Learning Enhanced Router

// Dynamic router enhanced with machine learning predictions
@Component
public class MLEnhancedDynamicRouter {

    @Autowired
    private RoutingMLService mlService;

    @Autowired
    private HistoricalDataService historicalDataService;

    @Autowired
    private FallbackRouter fallbackRouter;

    public RoutingDecision routeWithMLPrediction(MessageContext messageContext) {
        try {
            // Prepare features for ML model
            RoutingFeatures features = extractFeatures(messageContext);

            // Get ML prediction
            MLPrediction prediction = mlService.predictOptimalEndpoint(features);

            // Validate prediction confidence
            if (prediction.getConfidence() >= 0.8) {
                String predictedEndpoint = prediction.getPredictedEndpoint();

                // Verify endpoint availability
                if (isEndpointSuitable(predictedEndpoint, messageContext)) {
                    // Record successful ML routing for model feedback
                    recordMLRoutingDecision(messageContext, prediction, true);

                    return RoutingDecision.builder()
                        .targetEndpoint(predictedEndpoint)
                        .isDefinitive(true)
                        .reason("ML-predicted optimal endpoint")
                        .confidence(prediction.getConfidence())
                        .mlPrediction(prediction)
                        .build();
                }
            }

            // Fall back to traditional routing if ML prediction is uncertain or invalid
            RoutingDecision fallbackDecision = fallbackRouter.route(messageContext);

            // Record ML routing failure for model improvement
            recordMLRoutingDecision(messageContext, prediction, false);

            return fallbackDecision;

        } catch (Exception e) {
            log.warn("ML routing failed, falling back to traditional routing", e);
            return fallbackRouter.route(messageContext);
        }
    }

    private RoutingFeatures extractFeatures(MessageContext messageContext) {
        Map<String, Object> message = messageContext.getMessage();
        Map<String, Object> headers = messageContext.getHeaders();

        return RoutingFeatures.builder()
            .messageType((String) message.get("type"))
            .messageSize(calculateMessageSize(message))
            .priority((String) headers.get("priority"))
            .source((String) headers.get("source"))
            .timeOfDay(Instant.now().atZone(ZoneId.systemDefault()).getHour())
            .dayOfWeek(Instant.now().atZone(ZoneId.systemDefault()).getDayOfWeek().getValue())
            .recentSystemLoad(getRecentSystemLoad())
            .historicalPatterns(getHistoricalPatterns(messageContext))
            .build();
    }

    private void recordMLRoutingDecision(MessageContext context, MLPrediction prediction, 
                                       boolean successful) {
        MLFeedback feedback = MLFeedback.builder()
            .messageContext(context)
            .prediction(prediction)
            .successful(successful)
            .timestamp(Instant.now())
            .build();

        mlService.recordFeedback(feedback);
    }
}

@Service
public class RoutingMLService {

    private final MLModel routingModel;

    public MLPrediction predictOptimalEndpoint(RoutingFeatures features) {
        // Convert features to model input format
        double[] inputVector = featuresToVector(features);

        // Get model prediction
        ModelOutput output = routingModel.predict(inputVector);

        // Convert model output to routing decision
        return convertToRoutingPrediction(output, features);
    }

    public void recordFeedback(MLFeedback feedback) {
        // Store feedback for model retraining
        feedbackRepository.save(feedback);

        // Trigger model retraining if enough new feedback accumulated
        if (shouldRetrain()) {
            scheduleModelRetraining();
        }
    }

    @Scheduled(fixedRate = 86400000) // Daily
    public void retrainModel() {
        log.info("Starting ML model retraining");

        List<MLFeedback> recentFeedback = feedbackRepository.findRecentFeedback();

        if (recentFeedback.size() >= MIN_FEEDBACK_FOR_RETRAINING) {
            try {
                MLModel newModel = modelTrainer.trainModel(recentFeedback);

                if (newModel.getAccuracy() > routingModel.getAccuracy()) {
                    this.routingModel = newModel;
                    log.info("Updated routing model with improved accuracy: {}", 
                            newModel.getAccuracy());
                }
            } catch (Exception e) {
                log.error("Model retraining failed", e);
            }
        }
    }
}

4. Context-Aware Geographic Router

// Dynamic router that considers geographic and network topology factors
@Component
public class GeographicDynamicRouter {

    @Autowired
    private GeographicService geoService;

    @Autowired
    private NetworkTopologyService topologyService;

    @Autowired
    private DataResidencyService residencyService;

    public RoutingDecision routeByGeography(MessageContext messageContext, 
                                          String clientLocation) {

        // Check data residency requirements
        DataResidencyRequirement residencyReq = residencyService
            .getRequirements(messageContext.getMessage());

        if (residencyReq != null) {
            String requiredRegion = residencyReq.getRequiredRegion();
            List<String> allowedRegions = residencyReq.getAllowedRegions();

            // Filter endpoints by data residency compliance
            List<EndpointInfo> compliantEndpoints = filterByDataResidency(
                getAvailableEndpoints(), 
                requiredRegion, 
                allowedRegions
            );

            if (compliantEndpoints.isEmpty()) {
                return RoutingDecision.failure("No endpoints comply with data residency requirements");
            }

            // Select optimal endpoint within compliance constraints
            String selectedEndpoint = selectOptimalGeographicEndpoint(
                compliantEndpoints, 
                clientLocation
            );

            return RoutingDecision.builder()
                .targetEndpoint(selectedEndpoint)
                .isDefinitive(true)
                .reason("Geographic routing with data residency compliance")
                .build();
        }

        // Standard geographic optimization without residency constraints
        return routeByProximity(messageContext, clientLocation);
    }

    private RoutingDecision routeByProximity(MessageContext messageContext, 
                                           String clientLocation) {

        List<EndpointInfo> availableEndpoints = getAvailableEndpoints();

        Map<String, GeographicMetrics> geoMetrics = new HashMap<>();

        for (EndpointInfo endpoint : availableEndpoints) {
            GeographicMetrics metrics = calculateGeographicMetrics(
                clientLocation, 
                endpoint.getLocation()
            );
            geoMetrics.put(endpoint.getId(), metrics);
        }

        // Select endpoint with best geographic score
        String optimalEndpoint = geoMetrics.entrySet().stream()
            .min(Map.Entry.comparingByValue(
                Comparator.comparing(GeographicMetrics::getTotalScore)))
            .map(Map.Entry::getKey)
            .orElse(null);

        if (optimalEndpoint != null) {
            return RoutingDecision.builder()
                .targetEndpoint(optimalEndpoint)
                .isDefinitive(true)
                .reason("Geographic proximity optimization")
                .geographicMetrics(geoMetrics.get(optimalEndpoint))
                .build();
        }

        return RoutingDecision.failure("No suitable geographic endpoint found");
    }

    private GeographicMetrics calculateGeographicMetrics(String clientLocation, 
                                                       String endpointLocation) {

        // Calculate physical distance
        double distance = geoService.calculateDistance(clientLocation, endpointLocation);

        // Calculate network distance (latency-based)
        Duration networkLatency = topologyService.estimateLatency(clientLocation, endpointLocation);

        // Consider time zone differences for business hours
        int timezoneOffset = geoService.getTimezoneOffset(clientLocation, endpointLocation);

        // Calculate total geographic score (lower is better)
        double distanceScore = distance / 1000.0; // Normalize to thousands of km
        double latencyScore = networkLatency.toMillis() / 100.0; // Normalize to hundreds of ms
        double timezoneScore = Math.abs(timezoneOffset) / 12.0; // Normalize to half-day differences

        double totalScore = distanceScore + (latencyScore * 2.0) + timezoneScore;

        return GeographicMetrics.builder()
            .physicalDistance(distance)
            .networkLatency(networkLatency)
            .timezoneOffset(timezoneOffset)
            .distanceScore(distanceScore)
            .latencyScore(latencyScore)
            .timezoneScore(timezoneScore)
            .totalScore(totalScore)
            .build();
    }
}

Apache Camel Implementation

1. Basic Dynamic Router Route

@Component
public class BasicDynamicRouterRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("direct:dynamicRouter")
            .routeId("basic-dynamic-router")
            .log("Starting dynamic routing for message: ${header.messageId}")
            .dynamicRouter(method(DynamicRoutingService.class, "getNextEndpoint"))
            .log("Dynamic routing completed for message: ${header.messageId}");
    }
}

@Service
public class DynamicRoutingService {

    @Autowired
    private RoutingDecisionEngine decisionEngine;

    public String getNextEndpoint(
        @Header Map<String, Object> headers,
        @Property Map<String, Object> properties,
        @Body Object body
    ) {
        // Check if routing is complete
        if (properties.containsKey("routingComplete")) {
            return null; // Stop routing
        }

        // Create message context
        MessageContext context = MessageContext.builder()
            .headers(headers)
            .properties(properties)
            .body(body)
            .build();

        // Get routing decision
        RoutingDecision decision = decisionEngine.determineRoute(context);

        if (decision.isValid()) {
            // Mark routing as complete
            properties.put("routingComplete", true);

            log.info("Routing message to: {} (reason: {})", 
                    decision.getTargetEndpoint(), decision.getReason());

            return decision.getTargetEndpoint();
        }

        return null; // No route found
    }
}

2. Content and Context-Aware Dynamic Router

@Component
public class ContentAwareDynamicRouterRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("direct:contentAwareDynamicRouter")
            .routeId("content-aware-dynamic-router")
            .log("Processing message for content-aware routing")
            .process(exchange -> {
                // Analyze message content and set routing context
                Object body = exchange.getIn().getBody();
                Map<String, Object> headers = exchange.getIn().getHeaders();

                RoutingContext routingContext = analyzeMessageForRouting(body, headers);
                exchange.setProperty("routingContext", routingContext);
            })
            .dynamicRouter().method(ContentAwareRoutingService.class, "determineRoute")
            .log("Content-aware routing completed");
    }

    private RoutingContext analyzeMessageForRouting(Object body, Map<String, Object> headers) {
        RoutingContextBuilder builder = RoutingContext.builder();

        // Analyze content
        if (body instanceof Map) {
            Map<?, ?> messageData = (Map<?, ?>) body;

            builder.messageType((String) messageData.get("type"))
                   .priority((String) messageData.get("priority"))
                   .size(calculateMessageSize(body));

            // Extract business context
            if (messageData.containsKey("customer")) {
                Map<?, ?> customer = (Map<?, ?>) messageData.get("customer");
                builder.customerTier((String) customer.get("tier"))
                       .customerRegion((String) customer.get("region"));
            }
        }

        // Analyze headers
        builder.source((String) headers.get("source"))
               .timestamp(Instant.now())
               .correlationId((String) headers.get("correlationId"));

        return builder.build();
    }
}

@Service
public class ContentAwareRoutingService {

    @Autowired
    private RoutingRuleEngine ruleEngine;

    @Autowired
    private SystemStateService systemState;

    public String determineRoute(
        @Property("routingContext") RoutingContext context,
        @Property Map<String, Object> properties
    ) {
        // Check if routing is complete
        if (properties.containsKey("routingComplete")) {
            return null;
        }

        // Add current system state to context
        context = context.withSystemState(systemState.getCurrentState());

        // Apply routing rules
        RoutingDecision decision = ruleEngine.evaluateRouting(context);

        if (decision.hasRoute()) {
            properties.put("routingComplete", true);
            properties.put("routingDecision", decision);

            log.info("Content-aware routing decision: {} -> {} (confidence: {})", 
                    context.getMessageType(), decision.getTargetEndpoint(), 
                    decision.getConfidence());

            return decision.getTargetEndpoint();
        }

        return null;
    }
}

3. Load-Aware Dynamic Router

@Component
public class LoadAwareDynamicRouterRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("direct:loadAwareDynamicRouter")
            .routeId("load-aware-dynamic-router")
            .log("Starting load-aware dynamic routing")
            .process(exchange -> {
                // Gather current load information
                Map<String, LoadMetrics> loadMetrics = gatherLoadMetrics();
                exchange.setProperty("loadMetrics", loadMetrics);

                // Set routing attempts counter
                exchange.setProperty("routingAttempts", 0);
            })
            .dynamicRouter().method(LoadAwareRoutingService.class, "selectEndpoint")
            .log("Load-aware routing completed");
    }

    private Map<String, LoadMetrics> gatherLoadMetrics() {
        // Implementation to gather load metrics from all available endpoints
        return Map.of(
            "endpoint1", new LoadMetrics(0.3, 150, 500),
            "endpoint2", new LoadMetrics(0.7, 350, 500),
            "endpoint3", new LoadMetrics(0.1, 50, 500)
        );
    }
}

@Service
public class LoadAwareRoutingService {

    private static final int MAX_ROUTING_ATTEMPTS = 3;

    public String selectEndpoint(
        @Property("loadMetrics") Map<String, LoadMetrics> loadMetrics,
        @Property("routingAttempts") Integer attempts,
        @Properties Map<String, Object> properties
    ) {
        // Check termination conditions
        if (attempts >= MAX_ROUTING_ATTEMPTS || properties.containsKey("routingComplete")) {
            return null;
        }

        // Find endpoint with best load characteristics
        String selectedEndpoint = loadMetrics.entrySet().stream()
            .filter(entry -> entry.getValue().isAvailable())
            .filter(entry -> entry.getValue().getLoadFactor() < 0.8) // Under 80% capacity
            .min(Map.Entry.comparingByValue(
                Comparator.comparing(LoadMetrics::getLoadFactor)))
            .map(Map.Entry::getKey)
            .orElse(null);

        if (selectedEndpoint != null) {
            properties.put("routingComplete", true);
            properties.put("selectedEndpoint", selectedEndpoint);

            LoadMetrics metrics = loadMetrics.get(selectedEndpoint);
            log.info("Selected endpoint {} with load factor: {} (current/max: {}/{})",
                    selectedEndpoint, metrics.getLoadFactor(), 
                    metrics.getCurrentLoad(), metrics.getMaxCapacity());

            return selectedEndpoint;
        }

        // Increment attempts for next iteration
        properties.put("routingAttempts", attempts + 1);

        log.warn("No suitable endpoint found in attempt {}", attempts + 1);
        return "direct:loadBalancingFailed";
    }
}

4. Time-Based Dynamic Router

@Component
public class TimeBasedDynamicRouterRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("direct:timeBasedDynamicRouter")
            .routeId("time-based-dynamic-router")
            .log("Starting time-based dynamic routing")
            .process(exchange -> {
                // Add time-based context
                Instant now = Instant.now();
                ZonedDateTime localTime = now.atZone(ZoneId.systemDefault());

                exchange.setProperty("currentTime", now);
                exchange.setProperty("hourOfDay", localTime.getHour());
                exchange.setProperty("dayOfWeek", localTime.getDayOfWeek());
                exchange.setProperty("isBusinessHours", isBusinessHours(localTime));
            })
            .dynamicRouter().method(TimeBasedRoutingService.class, "routeByTime")
            .log("Time-based routing completed");
    }

    private boolean isBusinessHours(ZonedDateTime dateTime) {
        DayOfWeek dayOfWeek = dateTime.getDayOfWeek();
        int hour = dateTime.getHour();

        return dayOfWeek != DayOfWeek.SATURDAY &&
               dayOfWeek != DayOfWeek.SUNDAY &&
               hour >= 9 && hour < 17; // 9 AM to 5 PM
    }
}

@Service
public class TimeBasedRoutingService {

    @Value("${routing.business-hours.endpoints}")
    private List<String> businessHoursEndpoints;

    @Value("${routing.after-hours.endpoints}")
    private List<String> afterHoursEndpoints;

    @Value("${routing.weekend.endpoints}")
    private List<String> weekendEndpoints;

    public String routeByTime(
        @Property("isBusinessHours") Boolean isBusinessHours,
        @Property("dayOfWeek") DayOfWeek dayOfWeek,
        @Property("hourOfDay") Integer hourOfDay,
        @Properties Map<String, Object> properties,
        @Body Object messageBody
    ) {
        if (properties.containsKey("routingComplete")) {
            return null;
        }

        List<String> candidateEndpoints;
        String routingReason;

        // Determine candidate endpoints based on time
        if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
            candidateEndpoints = weekendEndpoints;
            routingReason = "Weekend routing";
        } else if (isBusinessHours) {
            candidateEndpoints = businessHoursEndpoints;
            routingReason = "Business hours routing";
        } else {
            candidateEndpoints = afterHoursEndpoints;
            routingReason = "After-hours routing";
        }

        // Select first available endpoint from candidates
        String selectedEndpoint = candidateEndpoints.stream()
            .filter(this::isEndpointAvailable)
            .findFirst()
            .orElse(null);

        if (selectedEndpoint != null) {
            properties.put("routingComplete", true);
            properties.put("routingReason", routingReason);

            log.info("Time-based routing: {} -> {} ({})", 
                    hourOfDay, selectedEndpoint, routingReason);

            return selectedEndpoint;
        }

        // Fallback to any available endpoint
        return selectFallbackEndpoint(candidateEndpoints);
    }

    private boolean isEndpointAvailable(String endpoint) {
        // Implementation to check endpoint availability
        return true; // Simplified for example
    }

    private String selectFallbackEndpoint(List<String> originalCandidates) {
        // Try to find any available endpoint outside the original candidates
        log.warn("Original time-based candidates unavailable, selecting fallback");
        return "direct:fallbackEndpoint";
    }
}

Best Practices

1. Router Design and Architecture

2. Configuration and Rule Management

3. Performance and Scalability

4. Monitoring and Observability

5. Testing and Validation

6. Security and Compliance

The Dynamic Router pattern is fundamental for building adaptive, intelligent integration solutions that can respond to changing conditions, optimize performance, and maintain high availability in complex enterprise environments while supporting evolving business requirements.

← Back to All Patterns