samedi 24 juin 2023

Java | OO Design | How to map target field from multiple source fields depending on different business conditions?

Context: I am working on a Java Spring Boot backend service, related to e-commerce domain. My service gets data from Order-Service(OS), and I have to extract some data to send it further. Extracted data class looks something like:

class TargetDTO {
    String orderId;
    String shipmentId;
    String trackingUrl;
    String deliveryDate;
    // so on..
}

The source data, which I get from OS is contained in a JsonNode , and hence is of free form or no defined schema as below:

class OSResponseDTO {
   String field1;
   String field2;
   JsonNode sourceData; // this is of our interest for this problem
   // so on..
}

There are 2 dimensions in mapping the data :

  1. Mapping criteria from source to target:

    • The data can be directly extracted from source field, e.g. orderId <=> sourceData.orderId
    • The data can be a combination of more than one field, e.g. shipmentId <=> sourceData.orderId + sourceData.shipments.number.
    • We may need to apply some logic, and using one or more source fields, e.g. map deliveryDate from sourceData jsonNode and convert to specific format as per geographical region.
  2. Same target data can be mapped from different json paths of sourceData , based upon business conditions, such as orderPlacedOnline, orderPlacedOffline, orderPaidOnline, orderPaidCash, orderPaidCard, orderOutForDelivery, orderDelayed, etc. There are, as of now, about 200 such conditions, which can be grouped in 8-10 schemas. For e.g. trackingUrl <=>

if(orderOutForDelivery) {
  trackingUrl = sourceData.orderDetails.url;
} 
else if(orderPlaced) {
  trackingUrl = sourceData.url;
}
.
.
.
else if(orderShipped) {
  trackingUrl = sourceData.shipments.url;
}
else {
}

Question: What possible design I can go with to implement this?

What I have tried One approach I could think of was - Having strategies for mapping each target attribute, and creating a Map of condition and strategies, as:

interface OrderIdStrategy {
  void mapOrderId();
}
class OrderIdStrategy1 implements OrderIdStrategy {
}
class OrderIdStrategy2 implements OrderIdStrategy {
}

interface TrackingUrlStrategy {
  void mapTrackingUrl();
}
class TrackingUrlStrategy1 implements TrackingUrlStrategy {
}
class TrackingUrlStrategy2 implements TrackingUrlStrategy {
}

class DataExtractor {
  Map<String, Set<String>> businessCaseAndUsedStrategiesMapping;

  @PostConstruct
  init() {
     businessCaseAndUsedStrategiesMapping.put("orderPlacedOnline", Set.of("OrderIdStrategy1", "TrackingUrlStrategy2"));
     businessCaseAndUsedStrategiesMapping.put("orderOutForDelivery", Set.of("OrderIdStrategy2", "TrackingUrlStrategy2"));
  // so on
  }

  mapOrderId(String businessCase) {
     businessCaseAndUsedStrategiesMapping.get(businessCase).mapOderId();
  }

  mapShipmentId(String businessCase) {
     businessCaseAndUsedStrategiesMapping.get(businessCase).mapShipmentId();
  }

  mapTrackingUrl(String businessCase) {
     businessCaseAndUsedStrategiesMapping.get(businessCase).mapTrackingUrl();
  }
}

But the unhappy part of this approach is that I have to create a map for all those 200 business cases; and these business conditions are likely to grow with future.

Aucun commentaire:

Enregistrer un commentaire