jeudi 8 décembre 2022

SpringBoot selecting the @Repository based on design pattern

Small question on Spring Boot, and how to use a design pattern in order to select the appropriate @Repository please.

Setup: I have a springboot project which does nothing but save a pojo. The "difficulty" is that I need to choose where to save the pojo, based on some info from the payload request.

I started with a first version, which looks like this:

    @RestController
    public class ControllerVersionOne {
    
        @Autowired private ElasticRepository elasticRepository;
        @Autowired private MongoRepository mongoRepository;
        @Autowired private RedisRepository redisRepository;
        
        //many more other repositories
    
        @PostMapping(path = "/save")
        public Mono<String> save(@RequestBody MyRequest myRequest) {
            String whereToSave = myRequest.getWhereToSave();
            MyPojo myPojo = new MyPojo(UUID.randomUUID().toString(), myRequest.getValue());
            if (whereToSave.equals("elastic")) {
                return elasticRepository.save(myPojo).map(s -> "");
            } else if (whereToSave.equals("mongo")) {
                return mongoRepository.save(myPojo).map(s -> "");
            } else if (whereToSave.equals("redis")) {
                return redisRepository.save(myPojo).map(s -> "");
                // many more if else
            } else {
                return Mono.just("unknown destination");
            }
        }
    
    }

With the appropriate @Configuration and @Repository for each and every database (I am showing 3 here, but imagine many

@Configuration
public class ElasticConfiguration extends ReactiveElasticsearchConfiguration {

@Repository
public interface ElasticRepository extends ReactiveCrudRepository<MyPojo, String> {


@Configuration
public class MongoConfiguration extends AbstractReactiveMongoConfiguration {

@Repository
public interface MongoRepository extends ReactiveMongoRepository<MyPojo, String> {


@Configuration
public class RedisConfiguration {

@Repository
public class RedisRepository { //note redis does not extends Reactive___Repository

And this first version is working fine. Very happy, meaning I am able to save the pojo to where it should be saved, as I am getting the correct repository bean, using this ugly if else structure. This structure is not very elegant, especially, not flexible at all.

This is why I went to refactor and change to this second version:

@RestController
public class ControllerVersionTwo {

    private ElasticRepository elasticRepository;
    private MongoRepository mongoRepository;
    private RedisRepository redisRepository;
    private Map<String, Function<MyPojo, Mono<MyPojo>>> designPattern;

    @Autowired
    public ControllerVersionTwo(ElasticRepository elasticRepository, MongoRepository mongoRepository, RedisRepository redisRepository) {
        this.elasticRepository = elasticRepository;
        this.mongoRepository = mongoRepository;
        this.redisRepository = redisRepository;
// many more repositories
        designPattern = new HashMap<>();
        designPattern.put("elastic", myPojo -> elasticRepository.save(myPojo));
        designPattern.put("mongo", myPojo -> mongoRepository.save(myPojo));
        designPattern.put("redis", myPojo -> redisRepository.save(myPojo));
//many more put
    }

    @PostMapping(path = "/save")
    public Mono<String> save(@RequestBody MyRequest myRequest) {
        String whereToSave = myRequest.getWhereToSave();
        MyPojo myPojo = new MyPojo(UUID.randomUUID().toString(), myRequest.getValue());
        return designPattern.get(whereToSave).apply(myPojo).map(s -> "");
    }

}

As you can see, I am leveraging a design pattern refactoring the if-else into a hashmap. Then, just getting the correct repository based on the where to save. Working fine, but please note, the map is a Map<String, Function<MyPojo, Mono<MyPojo>>>, as I cannot construct a map of Map<String, @Repository>:

With this second version, it is more elegant, but unfortunately, still not very flexible.

This is why I am having the idea to build a third version, where I can configure the map itself, via a spring boot property @value for hashmap:

Here is what I tried:

@RestController
public class ControllerVersionThree {

    @Value("#{${configuration.design.pattern.map}}")
    Map<String, String> configurationDesignPatternMap;

    private Map<String, Function<MyPojo, Mono<MyPojo>>> designPatternStrategy;

    public ControllerVersionThree() {
        convertConfigurationDesignPatternMapToDesignPatternStrategy(configurationDesignPatternMap, designPatternStrategy);
    }

    @PostMapping(path = "/save")
    public Mono<String> save(@RequestBody MyRequest myRequest) {
        String whereToSave = myRequest.getWhereToSave();
        MyPojo myPojo = new MyPojo(UUID.randomUUID().toString(), myRequest.getValue());
        return designPatternStrategy.get(whereToSave).apply(myPojo).map(s -> "successSavedAt " + myRequest.getWhereToSave() + s.getValue());
    }

}

And I would configure in the property file:

configuration.design.pattern.map={elastic:ElasticRepository, mongo:MongoRepository , redis:RedisRepository , ...}

Unfortunately, I am stuck. First of all, is this even possible? And if it is, (hopefully it is), how to achieve this conversion please?

Thank you for your help.

Aucun commentaire:

Enregistrer un commentaire