← Назад к вопросам

Каким образом собирали информацию со всех источников в текущем проекте?

1.0 Junior🔥 111 комментариев
#Soft Skills и карьера

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектурный подход к агрегации данных из разных источников

При разработке крупного проекта часто требуется интегрировать информацию из множества различных источников: базы данных, внешние API, кэши, системы очередей и прочее. Расскажу о практических подходах, которые я применял.

1. Паттерн Data Aggregator (Фасад для данных)

Это паттерн, при котором мы создаём единую точку входа для сбора данных из разных источников:

public class UserDataAggregator {
    private final UserRepository userRepository;
    private final OrderService orderService;
    private final AnalyticsClient analyticsClient;
    private final NotificationService notificationService;
    private final CacheManager cacheManager;

    public UserProfile getUserProfile(Long userId) {
        UserProfile cached = cacheManager.get("user:" + userId, UserProfile.class);
        if (cached != null) {
            return cached;
        }

        User user = userRepository.findById(userId);
        List<Order> orders = orderService.getUserOrders(userId);
        UserAnalytics analytics = analyticsClient.getAnalytics(userId);
        List<Notification> notifications = notificationService.getUnread(userId);

        UserProfile profile = UserProfile.builder()
            .user(user)
            .orders(orders)
            .analytics(analytics)
            .unreadNotifications(notifications)
            .build();

        cacheManager.put("user:" + userId, profile, Duration.ofMinutes(10));
        return profile;
    }
}

2. Асинхронная параллельная загрузка с CompletableFuture

Если операции независимы, используем параллелизм для ускорения:

public CompletableFuture<UserProfile> getUserProfileAsync(Long userId) {
    CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> userRepository.findById(userId));
    CompletableFuture<List<Order>> ordersFuture = CompletableFuture.supplyAsync(() -> orderService.getUserOrders(userId));
    CompletableFuture<UserAnalytics> analyticsFuture = CompletableFuture.supplyAsync(() -> analyticsClient.getAnalytics(userId));
    CompletableFuture<List<Notification>> notificationsFuture = CompletableFuture.supplyAsync(() -> notificationService.getUnread(userId));

    return CompletableFuture.allOf(userFuture, ordersFuture, analyticsFuture, notificationsFuture)
        .thenApply(v -> UserProfile.builder()
            .user(userFuture.join())
            .orders(ordersFuture.join())
            .analytics(analyticsFuture.join())
            .unreadNotifications(notificationsFuture.join())
            .build());
}

3. Стратегия кэширования многоуровневого

Используем локальный кэш (in-memory) и распределённый (Redis):

public class MultiLevelCacheAggregator {
    private final LoadingCache<String, UserProfile> localCache;
    private final RedisTemplate<String, UserProfile> redisCache;
    private final UserDataService dataService;

    public UserProfile getProfile(Long userId) throws ExecutionException {
        String key = "user:" + userId;
        
        UserProfile profile = localCache.getIfPresent(key);
        if (profile != null) {
            return profile;
        }
        
        profile = redisCache.opsForValue().get(key);
        if (profile != null) {
            localCache.put(key, profile);
            return profile;
        }
        
        profile = dataService.aggregateFromSources(userId);
        
        localCache.put(key, profile);
        redisCache.opsForValue().set(key, profile, Duration.ofHours(1));
        
        return profile;
    }
}

4. Обработка ошибок и fallback логика

Не все источники одинаково критичны:

public UserProfile getProfileWithFallbacks(Long userId) {
    User user = userRepository.findById(userId);
    List<Order> orders = tryGetOrders(userId);
    UserAnalytics analytics = tryGetAnalytics(userId);

    return UserProfile.builder()
        .user(user)
        .orders(orders)
        .analytics(analytics)
        .build();
}

private List<Order> tryGetOrders(Long userId) {
    try {
        return orderService.getUserOrders(userId);
    } catch (ServiceUnavailableException e) {
        logger.warn("Failed to fetch orders for user {}", userId, e);
        return Collections.emptyList();
    }
}

private UserAnalytics tryGetAnalytics(Long userId) {
    try {
        return analyticsClient.getAnalytics(userId);
    } catch (Exception e) {
        logger.warn("Failed to fetch analytics for user {}", userId, e);
        return UserAnalytics.empty();
    }
}

5. GraphQL подход для гибкой агрегации

Если клиенту нужны разные наборы данных, GraphQL оптимален:

@QueryResolver
public User getUser(Long id) {
    return userRepository.findById(id);
}

@QueryResolver
public List<Order> orders(User user) {
    return orderService.getUserOrders(user.getId());
}

@QueryResolver
public UserAnalytics analytics(User user) {
    return analyticsClient.getAnalytics(user.getId());
}

Ключевые принципы:

  • Кэширование на всех уровнях для снижения нагрузки
  • Параллелизм для независимых операций
  • Graceful degradation когда источник недоступен
  • Circuit breaker паттерн для защиты от cascading failures
  • Мониторинг время ответа каждого источника

Этот подход позволяет создавать надёжные и быстрые системы даже при множестве разнородных источников данных.