Ответ
Архитектура строилась на основе паттерна Адаптер (Adapter) и Агрегатор (Aggregator), что обеспечивало гибкость и единообразие обработки.
1. Определение общего контракта:
public interface DataSource {
List<DataRecord> fetchData() throws DataFetchException;
}
2. Реализация адаптеров для каждого источника: Каждый адаптер инкапсулировал специфичную логику подключения, аутентификации и парсинга.
@Component
public class RestApiAdapter implements DataSource {
@Override
public List<DataRecord> fetchData() {
// Вызов REST API, десериализация JSON в DataRecord
}
}
@Component
public class DatabaseAdapter implements DataSource {
@Override
public List<DataRecord> fetchData() {
// Выполнение SQL-запроса, маппинг ResultSet в DataRecord
}
}
3. Агрегация данных: Сервис-агрегатор собирал данные от всех источников.
@Service
public class DataAggregationService {
private final List<DataSource> dataSources;
public List<DataRecord> collectAllData() {
return dataSources.parallelStream() // Параллельный вызов
.map(this::fetchSafely) // Обработка ошибок по источнику
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
private List<DataRecord> fetchSafely(DataSource source) {
try {
return source.fetchData();
} catch (DataFetchException e) {
log.error("Failed to fetch from source: " + source, e);
return List.of(); // Возвращаем пустой список, не ломая весь процесс
}
}
}
Дополнительные механизмы для надежности:
- Асинхронность: Использование
CompletableFutureили реактивных стримов для параллельных запросов. - Повторные попытки (Retry): Применение
@Retryable(Spring Retry) или резервных стратегий для неустойчивых источников. - Кэширование: Кэширование ответов источников, которые редко обновляются.