Дизайн Spring Boot для обслуживания нескольких провайдеров (небольшие сервисы) - PullRequest
0 голосов
/ 10 февраля 2019

Я создаю загрузочное приложение Spring, и мне нужна помощь с дизайном моих сервисов.Я буду использовать велосипедный сервис в качестве примера.Давайте рассмотрим следующие конечные точки:

/api/bike/v1/info
/api/bike/v1/find

Конечные точки будут вызывать службу Bike, которая потенциально может вызывать различных поставщиков, таких как BikeProvider1, BikeProvider2 и т. Д. Поставщики будут указаны в свойствах приложения, это позволяетсменить провайдера без перезапуска приложения.

пример структуры

+ bike
     + Client
         - BikeRequest.java
         - BikeResponse.java
     + domain
         - Providers.java //providers enums
         - ServiceType.java // service type enums
        + Exceptions
     + Controller
        - BikeController.java
     + Service
        + Providers
            - BikeProviderService.java //interface
            - BikeProbider1ProviderService.java // implements interface
            - BikeProbider2ProviderService.java // implements interface
        - BikeService.java //interface
        - BikeServiceImpl.java

Properties.file
  bike.provider.primary: BikeProvider1
  bike.provider.fallback: BikeProvider2

Я также думаю, что в будущем я хочу иметь 2 провайдера, один из которых в качестве запасного варианта, если первый неотвечать на запросы.Но пока я буду использовать только основной.

Мой текущий дизайн таков: у меня есть enum-классы для моего типа сервиса и провайдеров.В BikeServiceImpl.java я динамически нахожу нужного мне провайдера и звоню.

*******************ENUM CLASSES **************************
public enum Provider {
    BIKEPROVIDER1 ("BikeProvider1"),
    BIKEPROVIDER2  ("BikeProbider2"),

    private String name;

    Provider(final String name) {
        this.name = name;
    }
}

public enum ServiceType {
    BIKEINFORMATION ("BikeInformation"),
    FINDBIKE ("FindBike");

    private String type;

    ServiceType(final String name) {
        this.type = name;
    }
}


*****************BikeProviderService interface**************************

// this is the interface that all providers will implement
public interface BikeProviderService {
    Optional<Bike> getBikeInformation(BikeRequest bike);
    Optional<Bike> findBike(BikeRequest bike);
}

*****************BikeServiceImpl**************************

public class BikeServiceImpl implements BikeService {
    // I autowired providers
    @Autowired
    private BikeProvider1 bikeProvider1;
    @Autowired
    private BikeProvider2 bikeProvider2;

    //Get the provider from the properties
    @Value("${bike.provider.primary:}")
    private String primaryProvider;

// This is the call from controller endpoint /api/bike/v1/info 
    @Override
    public Bike findBikeInformation(BikeRequest bike, ServiceType serviceType) throws ServiceProviderException, BikeNotFoundException {
        return executeBikeInformationWithProvider(primaryProvider, bike, serviceType);
    }

    private Bike executeBikeInformationWithProvider(String provider, BikeRequest bike, ServiceType serviceType) throws ServiceProviderException, BikeNotFoundException {
        // find the providers based on the property value
        Optional<BikeProviderService> bikeProviderService = providerImplementation(provider);

        Optional<Bike> bikeResponse;
        if (BikeProviderService.isPresent()) {
            try {
                bikeResponse = bikeProviderService.get().getBikeInformation(bike);
                if (bikeResponse.isPresent()) {
                    LogCacheService.logRequest(bikeResponse, provider, serviceType.getType());
                    return bikeResponse.get();
                }
            } catch (Exception ex) {
                throw new ServiceProviderException("message");
            }
        }

        throw new BikeNotFoundException("Bike Not found");
    }

// This is the call from controller endpoint /api/bike/v1/find 
    @Override
    public Bike findBike(BikeRequest bike, ServiceType serviceType) throws ServiceProviderException, BikeNotFoundException {
        return executeFindBikeWithProvider(primaryProvider, bike, serviceType) ;
    }

    private Bike executeFindBikeWithProvider(String provider, BikeRequest bike, ServiceType serviceType) throws ServiceProviderException, BikeNotFoundException {
       // find the providers based on the property value
        Optional<BikeProviderService> bikeProviderService = providerImplementation(provider);

        Optional<Bike> bikeResponse;
        if (bikeProviderService.isPresent()) {
            try {
                bikeResponse = bikeProviderService.get().findBike(bike);
                if (bikeResponse.isPresent()) {
                    LogCacheService.logRequest(bike, provider, serviceType.getType());
                    return bikeResponse.get();
                }
            } catch (Exception ex) {
                throw new ServiceProviderException("message");
            }
        }

        throw new BikeNotFoundException("Bike Not found");
    }

    private Optional<BikeProviderService> providerImplementation(String bikeProvider) {
        Provider provider = null;

        try {
            provider = Provider.valueOf(bikeProvider.toUpperCase());
        } catch (IllegalArgumentException | NullPointerException ex) {
            log.error("Bike:: invalid Provider {}" , bikeProvider);
        }

        if (provider != null) {
            switch (provider) {
                case BIKEPROVIDER1:
                    return Optional.ofNullable(bikeProvider1);
                case BIKEPROVIDER2:
                    return Optional.ofNullable(bikeProvider2);
                default:
                    return Optional.empty();
            }
        }
        return Optional.empty();
    }

Я очень стараюсь не дублировать код, но боюсь, что я делаю этот код слишком сложным и слишком сложным дляподдерживать.добавив нового провайдера прямо сейчас:

1.- add to the enum provider class
2.- add to the switch statement
3.- add the service and @AutoWire the class

Я попробовал фабричный шаблон, но не хочу каждый раз создавать новый экземпляр провайдера.и близок к имеющемуся у меня методу providerImplementation.

Кроме того, я создал методы execute / helper, потому что, как я упоминал ранее, я хотел бы использовать метод отката или восстановления с fallback.Provider вбудущее.Пример Если findBike завершается неудачно, я могу вызвать executeFindBikeWithProvider с запасным вариантом.executeFindBikeWithProvider(**fallbackProvider**, bike, serviceType)

спасибо Дайте мне знать, если вам нужно больше деталей.

1 Ответ

0 голосов
/ 11 февраля 2019

Разве это не хороший вариант использования Spring @Profile?

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

https://www.baeldung.com/spring-profiles

Вместо того чтобы использовать список поставщиков в файле свойств, вы можете изменить конфигурацию среды выполнения, используя разные профили.Это фактически позволит вам игнорировать или активировать ваших провайдеров в зависимости от настроек вашего профиля.

Таким образом, добавление нового провайдера потребует от вас только внедрения нового провайдера и его привязки к профилю.

...