Reputation: 307
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
Reputation: 70701
How about injecting a map of BaseUserService
s 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
Reputation: 5659
You can think of using Visitor Pattern for this.
Some ideas:
environment
in config.environments
becomes a Visitable.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:
BaseUserService
wouldn't need changing any of your controllers. Just change the Visitor class.BaseUserService
s. 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