ephemeralCoder
ephemeralCoder

Reputation: 307

Service Class Delegation

I have a basic controller(Spring MVC) that will get User data for my website. It has different endpoints that could search/add/update data in different environment datasources. My issue is that depending on configuration values I need to only read and return a list of User data from certain environments(among other functionalities omitted from this post).

I have set up a base interface for getting User detail, and have implementations for every datasource that I need to go out to.

public interface BaseUserService {
    //user service interface
}

@Service
public class EnvironmentAService implements BaseUserService {
    // do datasource specific dao stuff.
}

@Service
public class EnvironmentBService implements BaseUserService {
    // do datasource specific dao stuff.
}

@Service
public class EnvironmentCService implements BaseUserService {
    // do datasource specific dao stuff.
}

Currently in my controller I am doing something like this:

@RestController
@RequestMapping("api")
public class UserController {

    @Autowired
    ConfigurationService configurationService

    @Autowired
    BaseUserService environmentAService;

    @Autowired
    BaseUserService environmentBService;

    @Autowired
    BaseUserService environmentCService;

    @RequestMapping(value = "/users", method = RequestMethod.GET,  headers={"Accept=application/json,application/xml"})
    public List<User> userList(
            @RequestParam(value="configValue") String configValue,
            @RequestParam(value="username") String username,
            @RequestParam(value="lastName") String configValue,
            @RequestParam(value="dob") Date dateOfBirth
            ){

        Configuration config = configurationService.getConfiguration(configValue);

        if(config.value == null)
            throw new ConfigurationNotFoundException("Please enter a valid configuration value");

        List<User> userList = new ArrayList<User> users;

        if(config.environments.contains(Environment.A))
            userList.addAll(environmentAService.getUserList(username,configValue,dateOfBirth));
        if(config.environments.contains(Environment.B))
            userList.addAll(environmentBService.getUserList(username,configValue,dateOfBirth));
        if(config.environments.contains(Environment.C))
            userList.addAll(environmentCService.getUserList(username,configValue,dateOfBirth));

        return userList;
    }
}

This works fine, but I am looking for a more elegant solution, so the controller isn't so tied to the service classes. I was looking into delegate and decorator pattern to maybe house conditionals in one class, but these seem like they won't work, or I'm not exactly sure on how to implement it in my case.

Upvotes: 0

Views: 153

Answers (2)

casablanca
casablanca

Reputation: 70701

How about injecting a map of BaseUserServices instead of individual fields?

@Autowired
Map<Environment, BaseUserService> serviceMap;

...

for (Entry<Environment, BaseUserService> e : serviceMap.entrySet()) {
    if (config.environments.contains(e.getKey())) {
        userList.addAll(e.getValue().getUserList(username,configValue,dateOfBirth));
    }
}

This way you can inject new services without having to modify your controller.

Upvotes: 1

ketan vijayvargiya
ketan vijayvargiya

Reputation: 5659

You can think of using Visitor Pattern for this.

Some ideas:

  • Each environment in config.environments becomes a Visitable.
  • Create a Visitor, say, UpdateUserListFromServiceVisitor. The visit(Environment.A) method in it uses an instance of EnvironmentAService to fetch user-list. Similarly, visit(Environment.B) uses EnvironmentBService and visit(Environment.C) uses EnvironmentCService.

Advantages of this approach:

  • You get rid of that ugly if-else block in the controller.
  • Adding a new BaseUserService wouldn't need changing any of your controllers. Just change the Visitor class.
  • Currently, the operation you need is get user-lists from BaseUserServices. Adding more operations in future would only involve adding a new Visitor.

Caveats: - If config.environments can contain duplicate entries, the Visitor Pattern described above will make duplicate calls to corresponding BaseUserService. You can consider converting it to a Set.

Upvotes: 0

Related Questions