【问题标题】:Spring Boot design for service with multiple providers(small services)具有多个提供者的服务的 Spring Boot 设计(小型服务)
【发布时间】:2019-02-10 20:43:20
【问题描述】:

我正在创建一个 Spring Boot 应用程序,我需要帮助设计我的服务。 我将以自行车服务为例。让我们考虑这些端点:

/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 个提供者作为后备,以防第一个提供者没有响应。但现在,我只会使用主节点。

我目前的设计是这样的,我有我的服务类型和提供者的枚举类。在 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 失败,我可以使用后备调用 executeFidBikeWithProvider。 executeFindBikeWithProvider(**fallbackProvider**, bike, serviceType)

谢谢,如果您需要更多详细信息,请告诉我。

【问题讨论】:

    标签: java spring-boot design-patterns dry factory-pattern


    【解决方案1】:

    这不是 Spring @Profile 的一个很好的用例吗?

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

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

    您可以使用不同的配置文件更改运行时配置,而不是使用属性文件中的提供程序列表。这将有效地允许您根据您的个人资料设置忽略或启用您的提供程序。

    这样,添加新的提供者只需要您实现新的提供者并将其与配置文件相关联。

    【讨论】:

    • 我明白你的意思,但我如何检查哪个提供商正在运行/启用。?没有一个大的 elseif 检查每个提供者。
    • 您的意思是哪个提供程序正在运行或启用?您只是想知道用于记录目的吗?或者您希望您的客户能够知道哪些已启用?
    猜你喜欢
    • 2016-04-04
    • 1970-01-01
    • 2014-04-24
    • 2020-10-26
    • 2017-06-03
    • 2014-10-03
    • 1970-01-01
    • 2021-04-26
    • 2012-07-07
    相关资源
    最近更新 更多