Data Collector example using Strategy and Pipeline Pattern
Let's design a flexible and extensible solution to extract various data points from different sources or formats.
Strategy Pattern: Encapsulate data collection algorithms in separate classes (Data Collectors).
Pipeline Pattern: Break down the data extraction process into smaller, independent steps (each Data Collector). Process data sequentially for clarity and maintainability.
Create DataCollector interface along with a method collect which takes input as well as output object, which is to be updated.
DataCollector.java
public interface DataCollector {
void collect(InputData inputData, OutputData outputData);
}
Create several implementation (for eg. header collector, token collector, request collector etc) of the interface.
HeaderDataCollector.java- Collects headers and set in the output
@Component
public class HeaderDataCollector implements DataCollector {
@Override
public void collect(InputData inputData, OutputData outputData) {
var headers = inputData.getHeaders();
outputData.setHeaderHost(headers.get("x-host"));
outputData.setHeaderTraceId(headers.get("x-trace-id"));
outputData.setHeaderSpanId(headers.get("x-span-id"));
}
}
TokenDataCollector.java- Collects token metadata and set in the output
@Component
public class TokenDataCollector implements DataCollector {
@Override
public void collect(InputData inputData, OutputData outputData) {
// Add logic to extract claims from JWT token and Set the values accordingly.
// Sample values are provided below.
outputData.setUserId("claim.userid");
outputData.setUserIp("claim.ip");
outputData.setUserName("claim.subject");
outputData.setUserEmailId("claim.emailid");
outputData.setUserPreferredLanguage("claim.language");
}
}
HttpRequestDataCollector.java- Collects http request related parameters
@Component
public class HttpRequestDataCollector implements DataCollector {
@Override
public void collect(InputData inputData, OutputData outputData) {
outputData.setCookie(inputData.getCookie());
outputData.setHttpType(inputData.getHttpType());
outputData.setRequest(inputData.getRequest());
outputData.setResponse(inputData.getResponse());
outputData.setUrl(inputData.getUrl());
}
}
Now, we will create a pipeline class which goes through all the above implementation to extract and set data in the output.
DataCollectorPipeline.java
@RequiredArgsConstructor
@Component
public class DataCollectorPipeline {
private final List<DataCollector> dataCollectors;
public void collect(InputData inputData, OutputData outputData) {
dataCollectors.forEach(collector -> collector.collect(inputData, outputData));
}
}
@RequiredArgsConstructor will inject all the DataCollector beans into dataCollectors list
Pipeline can be called in the service class like below.
DataService.java
@RequiredArgsConstructor
@Service
public class DataService {
private final DataCollectorPipeline dataCollectorPipeline;
public OutputData extractData(InputData inputData) {
var outputData = new OutputData();
dataCollectorPipeline.collect(inputData, outputData);
return outputData;
}
}